码迷,mamicode.com
首页 > 其他好文 > 详细

2-D range sum query implementation(2-D segment tree)

时间:2015-06-27 06:26:03      阅读:169      评论:0      收藏:0      [点我收藏+]

标签:

Google interview question:一个二维数组,有两个方法,一个是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))。

 

2-D range sum query implementation(2-D segment tree)

标签:

原文地址:http://www.cnblogs.com/dshao/p/4603573.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!