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

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))。

时间: 2024-10-27 06:07:00

2-D range sum query implementation(2-D segment tree)的相关文章

[LeetCode] Range Sum Query 2D - Immutable

Very similar to Range Sum Query - Immutable, but we now need to compute a 2d accunulated-sum. In fact, if you work in computer vision, you may know a name for such an array --- Integral Image. To solve this problem, Stefan has already posted a very e

LeetCode:Range Sum Query - Immutable - 数组指定区间内的元素和

1.题目名称 Range Sum Query(数组指定区间内的元素和) 2.题目地址 https://leetcode.com/problems/range-sum-query-immutable/ 3.题目内容 英文:Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. 中文:给定一个数组nums,求出索引i和j之间元素的和,i一定是小于或等于j

Range Sum Query 2D - Mutable &amp; Immutable

Range Sum Query 2D - Mutable Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). The above rectangle (with the red border) is defined by (row1,

leetCode 303. Range Sum Query - Immutable | Dynamic Programming

303. Range Sum Query - Immutable Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. Example: Given nums = [-2, 0, 3, -5, 2, -1] sumRange(0, 2) -> 1 sumRange(2, 5) -> -1 sumRange(0, 5) -> -3 Note:

LeetCode Range Sum Query - Mutable

原题链接在这里:https://leetcode.com/problems/range-sum-query-mutable/ 题目: Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. The update(i, val) function modifies nums by updating the element at index i to v

leetcode Range Sum Query - Immutable

题目连接 https://leetcode.com/problems/range-sum-query-immutable/ Range Sum Query - Immutable Description Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. Example: Given nums = [-2, 0, 3, -5, 2, -1] su

303. Range Sum Query - Immutable

303. Range Sum Query - Immutable Total Accepted: 10632 Total Submissions: 44726 Difficulty: Easy Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. Example: Given nums = [-2, 0, 3, -5, 2, -1] sumRang

[LeetCode] Range Sum Query - Immutable &amp; Range Sum Query 2D - Immutable

Range Sum Query - Immutable Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. Example: Given nums = [-2, 0, 3, -5, 2, -1] sumRange(0, 2) -> 1 sumRange(2, 5) -> -1 sumRange(0, 5) -> -3 Note: You

[LeetCode] Range Sum Query 2D - Mutable 二维区域和检索 - 可变

Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, co