【Lintcode】840. Range Sum Query - Mutable

題目地址:

https://www.lintcode.com/problem/range-sum-query-mutable/description

給定一個數組,要求設計一個數據結構,支持區間和的查詢,以及單點修改。

樹狀數組(Fenwick Tree)是解決這個問題的經典數據結構。設原數組是AA,樹狀數組類似於前綴和數組,可以以O(logn)O(\log n)的時間計算出AA的前ii個數之和;但與前綴和數組不同的是,它可以以O(logn)O(\log n)的時間做單點更新(樹狀數組裏的單點更新的意思是,將原數組的第ii個位置的數增加Δ\Delta,對應的樹狀數組做對應的更新),而對於前綴和數組來說,做單點更新的時候會花費O(n)O(n)的時間。具體可以參考https://blog.csdn.net/qq_46105170/article/details/103870987。代碼如下:

public class NumArray {
    
    class FenwickTree {
        
        private int[] tree;
        
        public FenwickTree(int[] nums) {
        	// 樹狀數組和前綴和類似,需要開原數組長度多一個位置,
        	// 但多開一個位置的原因和前綴和數組不太一樣,它由樹狀數組本身的性質有關
            tree = new int[nums.length + 1];
            // 一開始樹狀數組對應的是元素全是0的數組,
            // 初始化的時候其實就是做若干次單點更新,即將每個數字加進去
            for (int i = 0; i < nums.length; i++) {
                add(i + 1, nums[i]);
            }
        }
        
        // 單點更新,將原數組的第i個數(從1開始數)增加d
        public void add(int i, int d) {
            while (i <= tree.length - 1) {
                tree[i] += d;
                i += lowbit(i);
            }
        }
        
        // 求原數組前i個數的和(從1開始數)
        public int sum(int i) {
            int sum = 0;
            while (i > 0) {
                sum += tree[i];
                i -= lowbit(i);
            }
            
            return sum;
        }
        
        private int lowbit(int x) {
            return x & -x;
        }
    }
    
    FenwickTree tree;
    // 還需要把給定數組的引用存一下,
    // 目的是,在做update的時候需要知道相比原數組增加的d是多少
    int[] nums;
    
    public NumArray(int[] nums) {
        tree = new FenwickTree(nums);
        this.nums = nums;
    }
    
    public void update(int i, int val) {
    	// 將nums[i]更新爲val,等價於將nums[i]增加val - nums[i],
    	// 對於樹狀數組來說,就是將第i + 1個數增加val - nums[i],
    	// 同時需要將原數組也做相應的變化,以便於下一次對樹狀數組做add操作
        tree.add(i + 1, val - nums[i]);
        nums[i] = val;
    }
    
    public int sumRange(int i, int j) {
    	// 返回原數組的第i + 1個數到第j + 1個數之和
        return tree.sum(j + 1) - tree.sum(i);
    }
}

預處理時間O(nlogn)O(n\log n),區間和查詢和單點修改時間都是O(logn)O(\log n),空間O(n)O(n)

註解:
類裏也可以不存原數組的引用,直接利用樹狀數組的sum方法也可以查到原數組某個位置的數,然後做update的時候就不需要更新原數組了。但這樣做的話時間會慢一點,因爲要多執行兩次sum操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章