我有一系列数字。我的目标是找到第一个元素的索引,它从起始索引到右边时大于某个值k。
例如,如果数组是A = [4 3 3 4 6 7 1]并且k = 3并且起始索引是1(基于0的索引),则大于k的第一个数的索引是3。 类似地,如果k = 3且起始索引= 0,则第一个元素的索引为0。
预处理很好,因为我需要为不同的k值和起始索引处理多个这样的查询。
[更新] 在任何“查找第一个索引”查询之间也可能存在一些数组更新查询。例如,index = 1且值为2的更新查询会将A更改为[4 5 3 4 6 7 1]
答案 0 :(得分:1)
根据数据和查询,实施天真的方法实际上可能更有效。转到起始索引,然后只需查找大于k的值。
myStringBuilder.ToString()
你应该先尝试一下。
如果您发现可以根据值(相对于starting_index)更快地缩小候选集的范围,您也可以尝试这种方法:
BuildString()
预处理步骤:使索引生成按排序值排序的数组索引。 (如果更新数组,则现在还必须更新索引。)
array = [4, 3, 3, 4, 6, 7, 1]
# eliminate candidates based on starting_index
candidate_set = [3, 3, 4, 6, 7, 1]
# find index of first element greater than k in linear time
result = 2 + starting_index
使用数组二分或二分搜索检索值大于k = 3的数组索引列表。
array = [4, 3, 3, 4, 6, 7, 1]
过滤掉起始索引不大于起始索引= 1的候选人。
# first column value, second column array index
index = [(1, 6), (3, 1), (3, 2), (4, 0), (4, 3), (6, 4), (7, 5)]
通过迭代此列表来选择最小的数组索引。
candidate_set = [(4, 0), (4, 3), (6, 4), (7, 5)]
如果您有兴趣花一些时间来节省CPU周期,可以添加memoization,甚至可以将所有可能查询的所有结果预先计算到查找表中。 (并考虑缓存失效。)
答案 1 :(得分:1)
如果您事先知道所有查询,则会有一个时间复杂度为O(m log n)的算法,其中m是查询数,n是元素数。
从头到尾迭代遍历数组,并保持出队结构。
i
处,我们尝试从出列的前面弹出所有小于索引i
当前值的元素。然后将索引i
附加到出列队列。我们可以很容易地看到出列的所有值都是按升序排列的。i
开始的所有查询,请在出列中使用二进制搜索来查找大于k
的第一个元素的伪代码:
Dequeue<Integer> q = new ArrayList<>();
for(int i = n - 1; i >= 0; i--){
while(!q.isEmpty() && q.peek() <= data[i]){
q.poll();
}
q.addFirst(i);
for all query start at i {
int st = 0;
int ed = q.size();
int re = -1;
while(st <= ed){
int mid = (st + ed)/2;
if(data[q.get(mid)] > k){
re = q.get(mid);
st = mid - 1;
}else{
ed = mid + 1;
}
}
print(re);
}
}
由于数组可以实时更新,因此我们需要使用Segment Tree来跟踪数组每个段中的最大元素。
对于每个查询,我们需要使用二进制搜索来搜索最大值大于k的最小段。
时间复杂度O(m log log n),其中m是查询数,n是元素数。
的伪代码:
Build segment tree from input array
for each query {
if update query{
update tree
}else{
int startIndex = starting index for this query;
int start = startIndex;
int end = ending index;
int re = -1;
while(start <= end){
int mid = (start + end)/2;
//Getting the maximum value in segment [startIndex, mid]
if(tree.maximumInSegment(startIndex, mid) > k){
re = mid;
end = mid - 1;
}else{
start = mid + 1;
}
}
print re;
}
}
答案 2 :(得分:1)
段树方法: 对于给定的值 x 和范围 a[l…r],找到范围 a[l…r] 中的最小 i,使得 a[i] 大于 x。
这个任务可以通过使用 Segment Tree 对最大前缀查询的二分搜索来解决。但是,这将导致 O(log^2(n)) 解决方案。
通过树下降找到位置:通过每次向左或向右移动,取决于左孩子的最大值。从而在 O(logn) 时间内找到答案。
int get_first(int v, int lv, int rv, int l, int r, int x) {
if(lv > r || rv < l) return -1;
if(l <= lv && rv <= r) {
if(t[v] <= x) return -1;
while(lv != rv) {
int mid = lv + (rv-lv)/2;
if(t[2*v] > x) {
v = 2*v;
rv = mid;
}else {
v = 2*v+1;
lv = mid+1;
}
}
return lv;
}
int mid = lv + (rv-lv)/2;
int rs = get_first(2*v, lv, mid, l, r, x);
if(rs != -1) return rs;
return get_first(2*v+1, mid+1, rv, l ,r, x);
}
有关段树的更多说明,请查看:Segment Tree
答案 3 :(得分:0)
算法步骤。