该死,这个解决方案无法更新元素,但至少找到了第 k 个元素,在这里你会得到一些想法,以便你可以想到一些提供更新的解决方案。尝试基于指针的 B 树。
这是 O(n log n) 空间和 O(q log^2 n) 时间复杂度。后来我用 O(log n) 对每个查询进行了解释。
因此,您需要执行以下操作:
1) 在给定数组上创建一个“段树”。
2) 对于每个节点,您将存储整个数组,而不是存储一个数字。该数组的大小必须等于它的孩子的数量。该数组(如您所料)必须包含底部节点(子节点或该段中的数字)的值,但已排序。
3) 要制作这样一个数组,您需要合并来自段树的两个子节点的两个数组。但不仅如此,对于刚刚创建的数组中的每个元素(通过合并),您需要记住数字在插入合并数组之前的位置(基本上,它来自的数组,以及其中的位置) .以及指向未从同一数组插入的第一个下一个元素的指针。
4) 使用这种结构,您可以检查在某些段 S 中有多少小于给定值 x 的数字。您可以找到(通过二分查找)根节点数组中的第一个数字 > = x。然后,使用您制作的指针,您可以在 O(1) 中找到两个子数组(前一个节点的子节点的数组)的相同问题的结果。您停止对代表给定段 S 内部或外部的完整段的每个节点进行此降序操作。时间复杂度为 O(log n): O(log n) 以找到 >=x 的第一个元素, 和 O(log n) 对于 S 的所有分解段。
5) 对解决方案进行二分搜索。
这是每个查询 O(log^2 n) 的解决方案。但是你可以减少到 O(log n):
1) 在执行我上面写的所有操作之前,您需要转换问题。您需要对所有数字进行排序并记住原始数组中每个数字的位置。现在这些位置代表您正在处理的数组。称该数组为 P。
如果查询段的边界是 a 和 b。您需要按值(而不是按索引)找到 P 中位于 a 和 b 之间的第 k 个元素。该元素代表您的结果在原始数组中的索引。
2) 要找到第 k 个元素,您需要进行某种类型的回溯,其复杂度为 O(log n)。您将按值询问索引 0 和(某些其他索引)之间的元素数量,这些元素介于 a 和 b 之间。
3) 假设您知道某个段 (0,h) 的此类问题的答案。为树中从 h 开始的所有部分获取相同类型问题的答案,从最大的部分开始。只要当前答案(来自段 (0,h))加上您最后得到的答案大于 k,就继续获得这些答案。然后更新 h。不断更新 h,直到树中只有一个以 h 开头的段。那 h 是你在你所说的问题中寻找的数字的索引。
要从树中获得某个片段的此类问题的答案,您将花费 O(1) 的时间。因为你已经知道它的父段的答案,并且使用我在第一个算法中解释的指针,你可以在 O(1) 中得到当前段的答案。