那些小而美的演算法技巧 字首和 差分陣列

2022-07-03 23:36:12 字數 3943 閱讀 6049

1109.航班預訂統計

-----------

前文 字首和技巧詳解 寫過的字首和技巧是非常常用的演算法技巧,字首和主要適用的場景是原始陣列不會被修改的情況下,頻繁查詢某個區間的累加和

沒看過前文沒關係,這裡簡單介紹一下字首和,核心**就是下面這段:

class prefixsum 

}/* 查詢閉區間 [i, j] 的累加和 */

prefix[i]就代表著nums[0..i-1]所有元素的累加和,如果我們想求區間nums[i..j]的累加和,只要計算prefix[j+1] - prefix[i]即可,而不需要遍歷整個區間求和。

ps:我認真寫了 100 多篇原創,手把手刷 200 道力扣題目,全部發布在 labuladong的演算法小抄,持續更新。建議收藏,按照我的文章順序刷題,掌握各種演算法套路後投再入題海就如魚得水了。

本文講乙個和字首和思想非常類似的演算法技巧「差分陣列」,差分陣列的主要適用場景是頻繁對原始陣列的某個區間的元素進行增減

比如說,我給你輸入乙個陣列nums,然後又要求給區間nums[2..6]全部加 1,再給nums[3..9]全部減 3,再給nums[0..4]全部加 2,再給...

一通操作猛如虎,然後問你,最後nums陣列的值是什麼?

常規的思路很容易,你讓我給區間nums[i..j]加上val,那我就乙個 for 迴圈給它們都加上唄,還能咋樣?這種思路的時間複雜度是 o(n),由於這個場景下對nums的修改非常頻繁,所以效率會很低下。

這裡就需要差分陣列的技巧,類似字首和技巧構造的prefix陣列,我們先對nums陣列構造乙個diff差分陣列,diff[i]就是nums[i]nums[i-1]之差

int diff = new int[nums.length];

// 構造差分陣列

通過這個diff差分陣列是可以反推出原始陣列nums的,**邏輯如下:

int res = new int[diff.length];

// 根據差分陣列構造結果陣列

res[0] = diff[0];

for (int i = 1; i < diff.length; i++)

這樣構造差分陣列diff,就可以快速進行區間增減的操作,如果你想對區間nums[i..j]的元素全部加 3,那麼只需要讓diff[i] += 3,然後再讓diff[j+1] -= 3即可:

原理很簡單,回想diff陣列反推nums陣列的過程,diff[i] += 3意味著給nums[i..]所有的元素都加了 3,然後diff[j+1] -= 3又意味著對於nums[j+1..]所有元素再減 3,那綜合起來,是不是就是對nums[i..j]中的所有元素都加 3 了

只要花費 o(1) 的時間修改diff陣列,就相當於給nums的整個區間做了修改。多次修改diff,然後通過diff陣列反推,即可得到nums修改後的結果。

現在我們把差分陣列抽象成乙個類,包含increment方法和result方法:

class difference 

}/* 給閉區間 [i,j] 增加 val(可以是負數)*/

public void increment(int i, int j, int val)

}public int result()

return res;}}

這裡注意一下increment方法中的 if 語句:

public void increment(int i, int j, int val) 

}

j+1 >= diff.length時,說明是對nums[i]及以後的整個陣列都進行修改,那麼就不需要再給diff陣列減val了。

ps:我認真寫了 100 多篇原創,手把手刷 200 道力扣題目,全部發布在 labuladong的演算法小抄,持續更新。建議收藏,按照我的文章順序刷題,掌握各種演算法套路後投再入題海就如魚得水了。

這裡看一下力扣第 1109 題「航班預訂統計」:

函式簽名如下:

int corpflightbookings(int bookings, int n)
這個題目就在那繞彎彎,其實它就是個差分陣列的題,我給你翻譯一下:

給你輸入乙個長度為n的陣列nums,其中所有元素都是 0。再給你輸入乙個bookings,裡面是若干三元組(i,j,k),每個三元組的含義就是要求你給nums陣列的閉區間[i-1,j-1]中所有元素都加上k。請你返回最後的nums陣列是多少?

ps:因為題目說的n是從 1 開始計數的,而陣列索引從 0 開始,所以對於輸入的三元組(i,j,k),陣列區間應該對應[i-1,j-1]

這麼一看,不就是一道標準的差分陣列題嘛?我們可以直接復用剛才寫的類:

int corpflightbookings(int bookings, int n) 

// 返回最終的結果陣列

return df.result();

}

這道題就解決了。

其實我覺得差分陣列和字首和陣列都是比較常見且巧妙的演算法技巧,分別適用不同的常見,而且是會者不難,難者不會。所以,關於差分陣列的使用,你學會了嗎?

學會的話,分享/點讚/在看三連走起?

_____________

字首和,差分演算法

其實可以把它理解為數學上的數列的前n項和 對於乙個一維陣列的字首和 我們定義對於乙個陣列a的字首和陣列s,s i a 1 a 2 a i 與一維字首和類似,設s i j 表示所有a i j 的和。1 i i,1 j j 有一點像 矩形的面積 那樣,把一整塊區域的值都加起來。一般用來求區間和。對於一維...

序列演算法 字首和與差分

一般的字首和是對於陣列的求和,即對陣列的某一子陣列進行求和,即sum i,j sum i sum j 1 不僅如此字首和還能用於任何含可逆運算的運算子包括異或xor。上述的子陣列和的問題屬於陣列固定,大量訪問的問題優化。下面給出乙個大量做區間加的情況 給定乙個序列a 有很多次操作,每個操作形如 a ...

字首和與差分 演算法詳解

1 字首和 其實可以把它理解為數學上的數列的前n項和 對於乙個一維陣列的字首和 我們定義對於乙個陣列a的字首和陣列s,s i a 1 a 2 a i 2 二維字首和 與一維字首和類似,設s i j 表示所有a i j 的和。1 i i,1 j j 有一點像 矩形的面積 那樣,把一整塊區域的值都加起來...