我有一个向量对(datatype = double),其中每个对是(a,b)并且小于b。对于数字x,我想找出向量中对的数目,其中a <= x <= b。
考虑向量大小约为10 ^ 6。
我的方法
对向量对进行排序,并在成对的“ a”上对x执行一次Lower_bound操作,然后从头开始迭代直到我的下界值,并检查满足x <= b的条件的“ b”的值。
时间复杂度
N(LogN),其中N是向量大小。
问题
我必须在这种方法效率低下的大型查询中执行此操作,因此还有什么更好的解决方案可以降低时间复杂度。
对不起,我的英语和问题格式都很差。
答案 0 :(得分:0)
首先,如果仅对这些对进行简单扫描,则复杂度为O(n)! O(n log n)来自排序,对于一次性操作,这只是开销。这甚至可能是最好的方法,如果您不重用结果,即使您仅执行一些查询,它仍可能比排序更好。确保您允许自己退出算法。
无论如何,让我们考虑您需要进行许多查询。然后,进行改进的一个相对明显的步骤是在排序后不逐步进行迭代。相反,您可以对下限进行二进制搜索。只需将序列分成两半即可。下限可以在任意一半中找到,您可以通过查看分区之间的中间元素来确定。递归直到找到第一个元素(该元素可能不包含搜索的值),因为它的起始值已经更大。
关于另一个方向,事情并不是那么容易。仅仅因为您按起始值对范围进行了排序,并不意味着也对结束值进行了排序。此外,可以在序列中混合匹配范围和不匹配范围,因此在这里您将必须执行线性扫描。
最后,一些注意事项:
答案 1 :(得分:0)
对于段树,二进制索引树,间隔树,这是一个非常典型的样式问题。
必须对数组arr
进行两项操作。
您在数组arr
上有两个操作:
1. 范围更新:Add(a, b): for(int i = a; i <= b; ++i) arr[i]++
2. 点查询:Query(x): return arr[x]
或者,您可以巧妙地提出问题。
1. 点更新:Add(a, b): arr[a]++; arr[b+1]--;
2. 范围查询:Query(x): return sum(arr[0], arr[1] ..... arr[x]);
在以上每种情况下,您都有一个O(n)运算和一个O(1)运算。
对于第二种情况,查询本质上是前缀总和计算。二叉索引树在此任务上特别有效。
Tutorial for Binary Indexed Trees
重要思想:阵列压缩
您确实提到向量的大小约为10^6
,因此有可能无法创建那么大的数组。如果您可以事先创建一个由所有a
和b
和x
组成的集合,则可以将它们转换为从1
到{ {1}}。
超级聪明的想法:MO的算法
仅当允许您离线解决问题时,才允许这样做。这意味着您可以将所有查询点size of set
用作输入,以任意顺序解决它们并存储解决方案,然后以正确的顺序打印解决方案。
如果您的情况如此,请提及,然后我将进一步详细说明。但是二叉索引树将比Mo算法更有效。
编辑:
由于间隔值的类型为x
,因此在使用我的解决方案之前必须将其转换为整数。让我举个例子
double
这里所有有趣的点都不都是可能的双打,而只是上述数字
Intervals = (1.1 to 1.9), (1.4 to 2.1)
Query Points = 1.5, 2.0
如果我们将它们映射为正整数:
= {1.1, 1.4, 1.5, 1.9, 2.0, 2.1}
然后,您可以使用段树/二进制索引树。
答案 2 :(得分:0)
除了前面的答案外,这里还建议如何准备范围以优化后续查找。这个想法可以归结为对所有明显不同的输入值预先计算结果,但是要知道何时值没有明显不同。
为说明我的意思,让我们考虑以下范围的顺序:
1, 3
1, 8
2, 4
2, 6
准备好的输出结构如下:
1, 2 -> 2
2, 3 -> 4
3, 4 -> 3
4, 6 -> 2
6, 8 -> 1
对于1, 2
范围内的任何数字,初始序列中都有两个匹配范围。对于2, 3
范围内的任何数字,有四个匹配项,以此类推。请注意,由于某些输入范围部分重叠,因此这里有五个范围。由于此处的每个范围的最终值也是下一个范围的初始值,因此可以优化最终值。然后结果看起来像一个简单的地图:
1 -> 2
2 -> 4
3 -> 3
4 -> 2
6 -> 1
8 -> 0
请注意,最后一个范围没有后面的一个,因此显式零是必要的。对于第一个之前的值,这是隐含的。为了找到某个值的结果,只需找到小于或等于该值的键即可。这是一个简单的O(log n)查找。
答案 3 :(得分:-1)
对于每对a,b,您都可以分解,使得对于特定值有效的范围数为a = + 1和b = -1。然后,in变成一个简单的O(log n)查找,以查看有多少范围包含搜索值。