摘要:對(duì)于一組一維數(shù)組解決前項(xiàng)和,如果使用的方法需要的時(shí)間來(lái)找到前項(xiàng)數(shù)字的和,但是可以用的時(shí)間來(lái)更新對(duì)應(yīng)數(shù)字的值但是仍然需要的時(shí)間來(lái)更新?tīng)砍兜较鄳?yīng)數(shù)字?jǐn)?shù)組的和,相反可以使用樹(shù)狀數(shù)組來(lái)降低運(yùn)行時(shí)間求數(shù)組內(nèi)一段數(shù)組的和,但同樣我們?cè)黾恿烁聵?shù)狀數(shù)組內(nèi)
對(duì)于一組一維數(shù)組解決前n項(xiàng)和,如果使用linear scan的方法, 需要O(n)的時(shí)間來(lái)找到前n項(xiàng)數(shù)字的和,但是可以用O(1)的時(shí)間來(lái)更新對(duì)應(yīng)數(shù)字的值,但是仍然需要Linear的時(shí)間來(lái)更新?tīng)砍兜较鄳?yīng)數(shù)字?jǐn)?shù)組的和,相反可以使用樹(shù)狀數(shù)組來(lái)降低運(yùn)行時(shí)間求數(shù)組內(nèi)一段數(shù)組的和,但同樣我們?cè)黾恿烁聵?shù)狀數(shù)組內(nèi)任意節(jié)點(diǎn)數(shù)值的時(shí)間。
樹(shù)狀數(shù)組(Binary Indexed Tree)中每個(gè)節(jié)點(diǎn)的值是原數(shù)組中一個(gè)或幾個(gè)數(shù)組的和,所以在原數(shù)組中進(jìn)行求和操作就是在樹(shù)狀數(shù)組中進(jìn)行節(jié)點(diǎn)的求和操作, 相對(duì)應(yīng)的時(shí)間復(fù)雜度為O(logN)
Binary Index Tree 基于二進(jìn)制編碼建立:
需要保持一個(gè)原始數(shù)組a[], 和新建立一個(gè)樹(shù)狀數(shù)組e[],相對(duì)應(yīng)的二進(jìn)制編碼:
1 : 1 a[1] 2 : 10 e[2] = e[1] + a[2]; 3 : 011 e[3] = a[3]; 4 : 110 e[4] = e[2] + e[3] + a[4]; 5 : 101 e[5] = a[5]; 6 : 110 e[6] = e[5] + e[6]; 7 : 111 e[7] = a[7]; 8 : 1000 e[8] = e[4] + e[6] + e[7] + a[8]; e[4] = a[1] + a[2] + a[3] + a[4];
如果二進(jìn)制表示中末尾有連續(xù)的0,則e[i]是原數(shù)組中2^k數(shù)值的和
因此可以通過(guò)計(jì)算e[i]的前驅(qū)和后繼節(jié)點(diǎn)來(lái)計(jì)算出相對(duì)應(yīng)e[i]的值,實(shí)現(xiàn)方法:
lowBit = (i & (-i))
因此,我們有一個(gè)數(shù)組:
int[] nums; public int[] NumArray(int[] nums) { this.nums = nums; //Binary Indexed Trees int[] tree = new int[nums.length + 1]; for(int i = 0; i < tree.length; i++) { sum = 0; lowBit = i & (-i); for(int j = i; i > i - lowBit; j--) { sum += sum + nums[j - i]; } tree[i] = sum; } public void update(int i, int val) { //更新差值,從后往前相加 int diff = val - nums[i]; nums[i] = val; i++; for(; i < tree.length; i++) { tree[i] += diff; } } public void sumRange(int i, int j) { return sum(i + j) - sum(i); } private int sumRange(int i, int j) { int sum = 0; for(int i = k; i > 0; i -= i & (-i)) { sum += tree[i]; } return sum; }
Binary Index Tree 主要運(yùn)用是 Range Sum with Mutable Elements:
Range Sum with Mutable Elements 1D
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 val.
Example:
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
class Solution{ int[] nums; int[] tree; int size; //time: O(nlogn) public RangeSumQueryMutable(int[] nums) { this.size = nums.length; this.tree = new int[size + 1]; this.nums = new int[size]; for(int i = 0; i < n; i++) { updateTree(i, nums[i]); } } public void updateTree(int i, int val) { i = i + 1; while(i < size) { tree[i] += val; i += i & (-i); //the last set bits, 2s compliment } } public void update(int i, int val) { if(size == 0) return; update(i, val - nums[i]); nums[i] = val; } public int sumRange(int i, int j) { if(i == 0) return j; return getSum(j) - getSum(i - 1); } private void getSum(int i) { int sum = 0; i = i + 1; while(i > 0) { sum += tree[i]; i -= i & (-i);//go to ancestor } return sum; }
Range Sum with Mutable Elements 2D
Given matrix =
[[3, 0, 1, 4, 2], [5, 6, 3, 2, 1], [1, 2, 0, 1, 5], [4, 1, 0, 1, 7], [1, 0, 3, 0, 5]]
sumRegion(2, 1, 4, 3) -> 8
update(3, 2, 2)
sumRegion(2, 1, 4, 3) -> 10
class Solution { int[][] nums; int[][] tree; int rows; int cols; public class RangeSum2D(int[][] nums) { rows = nums.length; cols = nums[0].length; nums = new int[rows][cols]; tree = new int[rows + 1][cols + 1]; for(int i = 0; i < rows; i++) { for(int j = 0; j < cols; j++) { update(nums[i][j], i, j); } } } public void update(int val, int row, int col) { int diff = val - nums[i][j]; nums[row][col] = val; for(int i = row + 1; i < rows; i += (i & (-i))) { for(int j = col + 1; i < cols; j += (j & (-j))) { tree[i][j] += diff; } } } public int sumRegion(int row1, int col1, int row2, int col2) { return getSum(row2 + 1, col2 + 1) + getSum(row1, col1) - getSum(row1, col2 + 1) - getSum(row2 + 1, col1); } private int getSum(int i, int j) { int sum = 0; for(int i = rows; i > 0; i -= (i & (-i))) { for(int j = cols; j > 0; j -= (j & (-j)) { sum += tree[i][j]; } } return sum; } //O(m * logn * n * logn)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68340.html
摘要:在上做了一道斐波那契數(shù)列求和的題目,做完之后做了一些簡(jiǎn)單的優(yōu)化和用另一種方法實(shí)現(xiàn)。動(dòng)態(tài)規(guī)劃解決方案斐波那契數(shù)列求和除了可以用遞歸的方法解決,還可以用動(dòng)態(tài)規(guī)劃的方法解決。 在codewars上做了一道斐波那契數(shù)列求和的題目,做完之后做了一些簡(jiǎn)單的優(yōu)化和用另一種方法實(shí)現(xiàn)。 題目 function fibonacci(n) { if(n==0 || n == 1) r...
摘要:那么,有了循環(huán),為什么還要用遞歸呢在某些情況下費(fèi)波納切數(shù)列,漢諾塔,使用遞歸會(huì)比循環(huán)簡(jiǎn)單很多很多話說(shuō)多了也無(wú)益,讓我們來(lái)感受一下遞歸吧。 遞歸介紹 本來(lái)預(yù)算此章節(jié)是繼續(xù)寫(xiě)快速排序的,然而編寫(xiě)快速排序往往是遞歸來(lái)寫(xiě)的,并且遞歸可能不是那么好理解,于是就有了這篇文章。 在上面提到了遞歸這么一個(gè)詞,遞歸在程序語(yǔ)言中簡(jiǎn)單的理解是:方法自己調(diào)用自己 遞歸其實(shí)和循環(huán)是非常像的,循環(huán)都可以改寫(xiě)成遞歸...
閱讀 3275·2021-09-22 16:06
閱讀 3262·2021-09-02 15:40
閱讀 644·2019-08-30 15:54
閱讀 1050·2019-08-26 12:22
閱讀 1393·2019-08-26 12:17
閱讀 2755·2019-08-26 12:09
閱讀 516·2019-08-26 10:20
閱讀 797·2019-08-23 16:28