摘要:題目要求可以先參考數(shù)組不發(fā)生變動時的題目。最后的葉節(jié)點為當(dāng)前數(shù)組的值,非葉結(jié)點則記錄了子數(shù)組的范圍以及該子數(shù)組中所有元素的和。它是一個非常輕量級的數(shù)據(jù)結(jié)構(gòu),而且?guī)缀蹙褪菫檫@種題目量身打造。
題目要求
</>復(fù)制代碼
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
Note:
The array is only modifiable by the update function.
You may assume the number of calls to update and sumRange function is distributed evenly.
可以先參考數(shù)組不發(fā)生變動時的題目。
這里的難度在于數(shù)組可以在中間出現(xiàn)變動,那么面對大容量數(shù)組的時候如何選擇一個合適的數(shù)據(jù)結(jié)構(gòu)及很重要。
思路一:map緩存最開始我們有兩種直觀的想法,一種是在插入時同時更新后面所有的和,這意味著O(n)的插入復(fù)雜度和O(1)的讀取復(fù)雜度。我決定選擇第二種方式,也就是采用類似日志的形式記錄下每一次的變更。這樣當(dāng)我們讀取的時候,再遍歷日志,將相關(guān)的變更結(jié)果添加到當(dāng)前的數(shù)值上。缺點是,變更很多以及數(shù)組很龐大時,效率依然很差。
這個方法超時了。
</>復(fù)制代碼
private int[] sum;
private int[] nums;
private Map log;
public NumArray(int[] nums) {
this.nums = nums;
sum = new int[nums.length];
for(int i = 0 ; i();
}
public void update(int i, int val) {
log.put(i, val - nums[i]);
}
public int sumRange(int i, int j) {
int origin = 0;
if(i==0) origin = sum[j];
else origin = sum[j] - sum[i-1];
for(Integer key : log.keySet()){
if(key>=i && key <= j){
origin += log.get(key);
}
}
return origin;
}
思路二:Segment Tree
我們將一個數(shù)組轉(zhuǎn)化為一棵樹,其中當(dāng)前的數(shù)組被均勻的分割并且分別用左子數(shù)組和右子數(shù)組構(gòu)建左子樹和右子樹。最后的葉節(jié)點為當(dāng)前數(shù)組的值,非葉結(jié)點則記錄了子數(shù)組的范圍以及該子數(shù)組中所有元素的和。
舉個例子說明一下:
假設(shè)當(dāng)前的數(shù)組為[1,2,5],則構(gòu)成的Segment Tree為:
</>復(fù)制代碼
8
/
3 5
/
1 2
這里先將[1,2,5]分割為[1,2]和[5]兩個子數(shù)組,然后分別構(gòu)造子樹。最后的樹如上所示。
</>復(fù)制代碼
class SegmentTreeNode{
int start;
int end;
SegmentTreeNode left;
SegmentTreeNode right;
int sum;
public SegmentTreeNode(int start, int end){
this.start = start;
this.end = end;
}
}
private SegmentTreeNode buildSegmentTree(int[] nums, int start, int end){
if(start > end) return null;
SegmentTreeNode cur = new SegmentTreeNode(start, end);
if(start == end) cur.sum = nums[start];
else{
int mid = (start + end) / 2;
cur.left = buildSegmentTree(nums, start, mid);
cur.right = buildSegmentTree(nums, mid+1, end);
cur.sum = cur.left.sum + cur.right.sum;
}
return cur;
}
private SegmentTreeNode root;
public NumArray(int[] nums) {
this.root = buildSegmentTree(nums, 0, nums.length-1);
}
public void update(int i, int val) {
update(root, i, val);
}
private void update(SegmentTreeNode root, int position, int val){
if(root.start == root.end){
root.sum = val;
}else{
int mid = (root.start + root.end) / 2;
if(position <= mid){
update(root.left, position, val);
}else{
update(root.right, position, val);
}
root.sum = root.left.sum + root.right.sum;
}
}
public int sumRange(int i, int j) {
return sumRange(root, i, j);
}
public int sumRange(SegmentTreeNode root, int i, int j){
if(root.start==i && root.end==j){
return root.sum;
}
int mid = (root.start + root.end )/2;
if(j<=mid){
return sumRange(root.left, i, j);
}else if(i>mid){
return sumRange(root.right, i, j);
}else{
return sumRange(root.left, i, mid) + sumRange(root.right, mid+1, j);
}
}
要想了解更多關(guān)于Segment Tree,請參考這篇文章。
思路三:Binary Indexed Tree網(wǎng)上有非常多的關(guān)于二進(jìn)制索引數(shù)樹的教程。它是一個非常輕量級的數(shù)據(jù)結(jié)構(gòu),而且?guī)缀蹙褪菫檫@種題目量身打造。可以先從這篇文章和這篇文章了解一下。
</>復(fù)制代碼
class NumArray {
int[] nums;
int[] BIT;
int n;
public NumArray(int[] nums) {
this.nums = nums;
n = nums.length;
BIT = new int[n + 1];
for (int i = 0; i < n; i++)
init(i, nums[i]);
}
//每次更新當(dāng)前節(jié)點的同時更新父節(jié)點
public void init(int i, int val) {
i++;
while (i <= n) {
BIT[i] += val;
i += (i & -i);
}
}
//更新當(dāng)前節(jié)點,同時將改變傳遞給父節(jié)點
void update(int i, int val) {
int diff = val - nums[i];
nums[i] = val;
init(i, diff);
}
//
public int getSum(int i) {
int sum = 0;
i++;
while (i > 0) {
sum += BIT[i];
i -= (i & -i);
}
return sum;
}
public int sumRange(int i, int j) {
return getSum(j) - getSum(i - 1);
}
}
想要了解更多開發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號!將會不定期的發(fā)放福利哦~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/68694.html
摘要:表現(xiàn)在二進(jìn)制上的規(guī)律每次加上最末尾的求末尾的就拿這個數(shù)和它的補(bǔ)碼于。還有要求不是,要轉(zhuǎn)換一下,和之前那道的思路差不多。這里用一個的表示從到的和。 Range Sum Query - Immutable Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), i...
摘要:假設(shè)有一個整數(shù)數(shù)組,計算下標(biāo)從到包含和的數(shù)字的和。求和的請求將會在同一個整數(shù)數(shù)組上多次請求。這一題思路很簡單,因為。而利用動態(tài)規(guī)劃則很容易知道。這里將原先的一維數(shù)組替換成二維數(shù)組。要求計算一個矩形內(nèi)的所有元素的值。 Range Sum Query Immutable Given an integer array nums, find the sum of the elements be...
Problem 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). https://leetcode.com/static/i...s...
Problem 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 sumRan...
摘要:對于一組一維數(shù)組解決前項和,如果使用的方法需要的時間來找到前項數(shù)字的和,但是可以用的時間來更新對應(yīng)數(shù)字的值但是仍然需要的時間來更新牽扯到相應(yīng)數(shù)字?jǐn)?shù)組的和,相反可以使用樹狀數(shù)組來降低運行時間求數(shù)組內(nèi)一段數(shù)組的和,但同樣我們增加了更新樹狀數(shù)組內(nèi) 對于一組一維數(shù)組解決前n項和,如果使用linear scan的方法, 需要O(n)的時間來找到前n項數(shù)字的和,但是可以用O(1)的時間來更新對應(yīng)數(shù)...
閱讀 1811·2021-11-24 09:39
閱讀 2295·2021-09-30 09:47
閱讀 4162·2021-09-22 15:57
閱讀 1881·2019-08-29 18:36
閱讀 3584·2019-08-29 12:21
閱讀 596·2019-08-29 12:17
閱讀 1272·2019-08-29 11:25
閱讀 732·2019-08-28 18:26