您的位置:首页 > 产品设计 > UI/UE

Google interview question: 2-D range sum query(2-D segment tree)

2015-06-27 05:14 691 查看
一个二维数组,有两个方法,一个是update(x,y),更新一个cell的值,一个是query(x1,y1,x2,y2),查询(x1,y1,x2,y2)矩形内所有元素的和。

Senario 1. update调用次数远大于query。

Senario 2. query调用次数远大于update。

Senario 3. 两种方法调用一样多。

对于senario 1,只需要用一个二维数组储存数据,update相应的cell,时间复杂度O(1),query计算范围内所有元素的和,时间复杂度O(N)。

对于senario 2,可以建立一个四维的数组,储存每一个submatrix元素的和,时间复杂度O(N^2),update对应的cell会更新所有包含这个cell的submatrix,时间复杂度为O(N^2),query直接读取相应的submatrix,时间复杂度为O(1)。

对于senario 3,我们需要平衡查询与更新的复杂度。2-D segment tree是一个理想的数据结构来解决RSQ(range sum query), RMQ(range mininum query)这类问题。

在实现2-D segment tree之前,首先需要了解segment tree的原理与实现。介绍segment tree的文章已经有很多。
https://www.youtube.com/watch?v=ZBHKZF5w4YU
这个视频介绍了segment tree,用数组实现,十分简洁易懂。

public class SegmentTree {
public static void main(String arcg[]){
int[] input = {-1,0,3,6,2};
int[] segmentTree = new int[nextPowerOf2(input.length)*2-1];
Arrays.fill(segmentTree, Integer.MAX_VALUE);
constructSegmentTreeForRangeSumQuery(input, segmentTree, 0, input.length-1, 0);
System.out.println(Arrays.toString(segmentTree));
System.out.println(rangeSumQuery(segmentTree, 1, 3, 0, input.length-1, 0));
updateSegmentTreeForRangeSumQuery(segmentTree, 2, 1, 0, input.length-1, 0);
System.out.println(Arrays.toString(segmentTree));
System.out.println(rangeSumQuery(segmentTree, 1, 3, 0, input.length-1, 0));
}
public static int nextPowerOf2(int n){
n--;
n |= n>>1;
n |= n>>2;
n |= n>>4;
n |= n>>8;
n |= n>>16;
return ++n;
}
public static void constructSegmentTreeForRangeMinQuery(int[] input, int[] segmentTree, int low, int high, int pos){
if(low == high){
segmentTree[pos] = input[low];
return;
}
int mid = low+(high-low)/2;
constructSegmentTreeForRangeMinQuery(input, segmentTree, low, mid, 2*pos+1);
constructSegmentTreeForRangeMinQuery(input, segmentTree, mid+1, high, 2*pos+2);
segmentTree[pos] = Math.min(segmentTree[2*pos+1], segmentTree[2*pos+2]);
}
public static void constructSegmentTreeForRangeSumQuery(int[] input, int[] segmentTree, int low, int high, int pos){
if(low == high){
segmentTree[pos] = input[low];
return;
}
int mid = low+(high-low)/2;
constructSegmentTreeForRangeSumQuery(input, segmentTree, low, mid, 2*pos+1);
constructSegmentTreeForRangeSumQuery(input, segmentTree, mid+1, high, 2*pos+2);
segmentTree[pos] = segmentTree[2*pos+1]+segmentTree[2*pos+2];
}
public static int rangeMinQuery(int[] segmentTree, int qlow, int qhigh, int low, int high, int pos){
if(qlow<=low && qhigh>=high) return segmentTree[pos];
if(qlow > high || qhigh < low) return Integer.MAX_VALUE;
int mid = low+(high-low)/2;
return Math.min(rangeMinQuery(segmentTree, qlow, qhigh, low, mid, 2*pos+1), rangeMinQuery(segmentTree, qlow, qhigh, mid+1, high, 2*pos+2));
}
public static int rangeSumQuery(int[] segmentTree, int qlow, int qhigh, int low, int high, int pos){
if(qlow<=low && qhigh>=high) return segmentTree[pos];
if(qlow > high || qhigh < low) return 0;
int mid = low+(high-low)/2;
return rangeSumQuery(segmentTree, qlow, qhigh, low, mid, 2*pos+1) + rangeSumQuery(segmentTree, qlow, qhigh, mid+1, high, 2*pos+2);
}
public static void updateSegmentTreeForRangeMinQuery(int[] segmentTree, int index, int newVal, int low, int high, int pos){
if(index<low || index>high) return;
if(low == high){
segmentTree[pos] = newVal;
return;
}
int mid = low+(high-low)/2;
updateSegmentTreeForRangeMinQuery(segmentTree, index, newVal, low, mid, 2*pos+1);
updateSegmentTreeForRangeMinQuery(segmentTree, index, newVal, mid+1, high, 2*pos+2);
segmentTree[pos] = Math.min(segmentTree[2*pos+1], segmentTree[2*pos+2]);
}
public static void updateSegmentTreeForRangeSumQuery(int[] segmentTree, int index, int newVal, int low, int high, int pos){
if(index<low || index>high) return;
if(low == high){
segmentTree[pos] = newVal;
return;
}
int mid = low+(high-low)/2;
updateSegmentTreeForRangeSumQuery(segmentTree, index, newVal, low, mid, 2*pos+1);
updateSegmentTreeForRangeSumQuery(segmentTree, index, newVal, mid+1, high, 2*pos+2);
segmentTree[pos] = segmentTree[2*pos+1] + segmentTree[2*pos+2];
}
}


以上是对segment tree的实现,现在需要将其扩展到二维的情况。

2-D segment tree的原理和segment tree一样,只是从二叉树变成了四叉树,因此2-D segment tree也是quadtree,类似的还有octree,广泛运用于图形学中。



同样,2-D segment tree也可以方便的用数组实现。其实现的原理即segment tree的二维推广。

public class SegmentTree2D {
public static void main(String arcg[]){
int[][] input = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
int[] segmentTree = new int[nextPowerOf4(input.length*input[0].length)*2];
Arrays.fill(segmentTree, Integer.MAX_VALUE);
construct2DSegmentTree(input, segmentTree, 0, 0, input.length-1, input[0].length-1, 0);
System.out.println(Arrays.toString(segmentTree));
System.out.println(rangeSumQuery(segmentTree, 1, 0, 3, 2, 0, 0, input.length-1, input[0].length-1, 0));
update2DSegmentTree(segmentTree, 2, 1, 8, 0, 0, input.length-1, input[0].length-1, 0);
System.out.println(Arrays.toString(segmentTree));
}
public static int nextPowerOf4(int n){
int res = 1;
while(res<n) res <<= 1;
return res;
}
public static void construct2DSegmentTree(int[][] input, int[] segmentTree, int lowx, int lowy, int highx, int highy, int pos){
if(lowx == highx && lowy == highy){
segmentTree[pos] = input[lowx][lowy];
return;
}
int midx = lowx+(highx-lowx)/2, midy = lowy+(highy-lowy)/2;
construct2DSegmentTree(input, segmentTree, lowx, lowy, midx, midy, 4*pos+1);
construct2DSegmentTree(input, segmentTree, lowx, midy+1, midx, highy, 4*pos+2);
construct2DSegmentTree(input, segmentTree, midx+1, midy+1, highx, highy, 4*pos+3);
construct2DSegmentTree(input, segmentTree, midx+1, lowy, highx, midy, 4*pos+4);
segmentTree[pos] = segmentTree[4*pos+1]+segmentTree[4*pos+2]+segmentTree[4*pos+3]+segmentTree[4*pos+4];
}
public static int rangeSumQuery(int[] segmentTree, int qlowx, int qlowy, int qhighx, int qhighy, int lowx, int lowy, int highx, int highy, int pos){
if(qlowx<=lowx && qlowy<=lowy && qhighx>=highx && qhighy>=highy) return segmentTree[pos];
if(qlowx>highx || qhighx<lowx || qlowy>highy || qhighy<lowy) return 0;
int midx = lowx+(highx-lowx)/2, midy = lowy+(highy-lowy)/2;
return rangeSumQuery(segmentTree, qlowx, qlowy, qhighx, qhighy, lowx, lowy, midx, midy, 4*pos+1) +
rangeSumQuery(segmentTree, qlowx, qlowy, qhighx, qhighy, lowx, midy+1, midx, highy, 4*pos+2) +
rangeSumQuery(segmentTree, qlowx, qlowy, qhighx, qhighy, midx+1, midy+1, highx, highy, 4*pos+3) +
rangeSumQuery(segmentTree, qlowx, qlowy, qhighx, qhighy, midx+1, lowy, highx, midy, 4*pos+4);
}
public static void update2DSegmentTree(int[] segmentTree, int xindex, int yindex, int newVal, int lowx, int lowy, int highx, int highy, int pos){
if(xindex<lowx || xindex>highx || yindex<lowy || yindex>highy) return;
if(lowx == highx && lowy == highy) {
segmentTree[pos] = newVal;
return;
}
int midx = lowx+(highx-lowx)/2, midy = lowy+(highy-lowy)/2;
update2DSegmentTree(segmentTree, xindex, yindex, newVal, lowx, lowy, midx, midy, 4*pos+1);
update2DSegmentTree(segmentTree, xindex, yindex, newVal, lowx, midy+1, midx, highy, 4*pos+2);
update2DSegmentTree(segmentTree, xindex, yindex, newVal, midx+1, midy+1, highx, highy, 4*pos+3);
update2DSegmentTree(segmentTree, xindex, yindex, newVal, midx+1, lowy, highx, midy, 4*pos+4);
segmentTree[pos] = segmentTree[4*pos+1]+segmentTree[4*pos+2]+segmentTree[4*pos+3]+segmentTree[4*pos+4];
}
}


运用2-D segment tree,对于senario 3,update的时间复杂度为O(log(N)),query的时间复杂度为O(log(N))。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: