歡迎拜訪:霧里看山-CSDN博客
本篇主題:算法思想之前綴和(二)
發布時間:2025.4.11
隸屬專欄:算法
目錄
- 滑動窗口算法介紹
- 核心思想
- 大致步驟
- 例題
- 和為 K 的子數組
- 題目鏈接
- 題目描述
- 算法思路
- 代碼實現
- 和可被 K 整除的子數組
- 題目鏈接
- 題目描述
- 算法思路
- 代碼實現
- 連續數組
- 題目鏈接
- 題目描述
- 算法思路
- 代碼實現
- 矩陣區域和
- 題目鏈接
- 題目描述
- 算法思路
- 代碼實現
滑動窗口算法介紹
核心思想
前綴和(Prefix Sum
) 是一種預處理數組的方法,通過預先計算并存儲數組的累積和,將區間和查詢的時間復雜度從 O(n)
優化至 O(1)
,適用于頻繁查詢子數組和的場景。
大致步驟
- 預處理出來一個前綴和數組
- 使用前綴和數組
- 處理邊界情況
例題
和為 K 的子數組
題目鏈接
560. 和為 K 的子數組
題目描述
給你一個整數數組 nums 和一個整數 k ,請你統計并返回 該數組中和為 k 的子數組的個數 。
子數組是數組中元素的連續非空序列。
示例 1:輸入:nums = [1,1,1], k = 2
輸出:2示例 2:
輸入:nums = [1,2,3], k = 3
輸出:2提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
算法思路
設 i
為數組中的任意位置,用 sum[i]
表示 [0, i]
區間內所有元素的和。
想知道有多少個以 i
為結尾的和為 k
的子數組,就要找到有多少個起始位置為 x1, x2, x3...
使得 [x, i]
區間內的所有元素的和為 k
。那么 [0, x]
區間內的和是不是就是sum[i] - k
了。于是問題就變成:
- 找到在
[0, i - 1]
區間內,有多少前綴和等于sum[i] - k
的即可。
我們不用真的初始化一個前綴和數組,因為我們只關心在 i
位置之前,有多少個前綴和等于sum[i] - k
。因此,我們僅需用一個哈希表,一邊求當前位置的前綴和,一邊存下之前每一種前綴和出現的次數。
代碼實現
class Solution {
public:int subarraySum(vector<int>& nums, int k) {unordered_map<int, int> hash;hash[0] = 1;int n = nums.size(), sum = 0, ret = 0;for(int i = 0; i < n; i++){sum += nums[i];if(hash[sum-k] > 0)ret += hash[sum-k];hash[sum]++;}return ret;}
};
和可被 K 整除的子數組
題目鏈接
974. 和可被 K 整除的子數組
題目描述
給定一個整數數組
nums
和一個整數k
,返回其中元素之和可被k
整除的非空 子數組 的數目。
子數組 是數組中 連續 的部分。
示例 1:輸入:nums = [4,5,0,-2,-3,1], k = 5
輸出:7
解釋:
有 7 個子數組滿足其元素之和可被 k = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]示例 2:
輸入: nums = [5], k = 9
輸出: 0提示:
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
2 <= k <= 104
算法思路
補充兩個小知識
- 同余定理
如果(a - b) % n == 0
,那么我們可以得到一個結論:a % n == b % n
。用文字敘述就是,如果兩個數相減的差能被n
整除,那么這兩個數對n
取模的結果相同。
例如:(26 - 2) % 12 == 0
,那么26 % 12 == 2 % 12 == 2
。 - c++ 中負數取模的結果,以及如何修正負數取模的結果
- c++ 中關于負數的取模運算,結果是把負數當成正數,取模之后的結果加上一個負號。
例如:-1 % 3 = -(1 % 3) = -1
- 因為有負數,為了防止發生出現負數的結果,以
(a % n + n) % n
的形式輸出保證為正。
例如:-1 % 3 = (-1 % 3 + 3) % 3 = 2
- c++ 中關于負數的取模運算,結果是把負數當成正數,取模之后的結果加上一個負號。
設 i 為數組中的任意位置,用 sum[i]
表示 [0, i]
區間內所有元素的和。
- 想知道有多少個以
i
為結尾的可被k
整除的子數組,就要找到有多少個起始位置為x1, x2, x3...
使得[x, i]
區間內的所有元素的和可被k
整除。 - 設
[0, x - 1]
區間內所有元素之和等于a
,[0, i]
區間內所有元素的和等于b
,可得(b - a) % k == 0
。 - 由同余定理可得,
[0, x - 1]
區間與[0, i]
區間內的前綴和同余。于是問題就變成:- 找到在
[0, i - 1]
區間內,有多少前綴和的余數等于sum[i] % k
的即可。
- 找到在
我們不用真的初始化一個前綴和數組,因為我們只關心在 i
位置之前,有多少個前綴和等于sum[i] - k
。因此,我們僅需用一個哈希表,一邊求當前位置的前綴和,一邊存下之前每一種前綴和出現的次數。
代碼實現
class Solution {
public:int subarraysDivByK(vector<int>& nums, int k) {unordered_map<int, int> hash;hash[0%k] = 1;int sum = 0, ret = 0;for(auto &n : nums){sum += n;int r = (sum%k + k)%k;if(hash[r] > 0)ret+=hash[r];hash[r]++;} return ret;}
};
連續數組
題目鏈接
525. 連續數組
題目描述
給定一個二進制數組
nums
, 找到含有相同數量的0
和1
的最長連續子數組,并返回該子數組的長度。
示例 1:
輸入:nums = [0,1]
輸出:2
說明:[0, 1] 是具有相同數量 0 和 1 的最長連續子數組。示例 2:
輸入:nums = [0,1,0]
輸出:2
說明:[0, 1] (或 [1, 0]) 是具有相同數量 0 和 1 的最長連續子數組。示例 3:
輸入:nums = [0,1,1,1,1,1,0,0,0]
輸出:6
解釋:[1,1,1,0,0,0] 是具有相同數量 0 和 1 的最長連續子數組。
提示:
1 <= nums.length <= 105
nums[i]
不是0
就是1
算法思路
稍微轉化一下題目,就會變成我們熟悉的題:
- 本題讓我們找出一段連續的區間,
0
和1
出現的次數相同。 - 如果將
0
記為-1
,1
記為1
,問題就變成了找出一段區間,這段區間的和等于0
。
? 于是,就和 560. 和為 K 的子數組 這道題的思路一樣
代碼實現
class Solution {
public:int findMaxLength(vector<int>& nums) {unordered_map<int, int> hash;hash[0] = -1;int n = nums.size(), sum = 0, ret = 0;for(int i = 0; i < n; i++){sum += (nums[i] == 0 ? -1: 1);if(hash.count(sum))ret = max(ret, i - hash[sum]);else hash[sum] = i;}return ret;}
};
矩陣區域和
題目鏈接
1314. 矩陣區域和
題目描述
給你一個
m x n
的矩陣mat
和一個整數k
,請你返回一個矩陣answer
,其中每個answer[i][j]
是所有滿足下述條件的元素mat[r][c]
的和:
i - k <= r <= i + k
,j - k <= c <= j + k
且(r, c)
在矩陣內。示例 1:
輸入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 1
輸出:[[12,21,16],[27,45,33],[24,39,28]]示例 2:
輸入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 2
輸出:[[45,45,45],[45,45,45],[45,45,45]]提示:
m == mat.length
n == mat[i].length
1 <= m, n, k <= 100
1 <= mat[i][j] <= 100
算法思路
?維前綴和的簡單應用題,關鍵就是我們在填寫結果矩陣的時候,要找到原矩陣對應區域的左上角以及右下角的坐標(推薦畫圖)
- 左上角坐標:
x1 = i - k
,y1 = j - k
,但是由于會超過矩陣的范圍,因此需要對0
取一個max
。因此修正后的坐標為:x1 = max(0, i - k)
,y1 = max(0, j - k)
; - 右下角坐標:
x1 = i + k
,y1 = j + k
,但是由于會超過矩陣的范圍,因此需要對row - 1
,以及col - 1
取?個min
。因此修正后的坐標為:x2 = min(row - 1, i + k)
,y2 = min(col - 1, j + k)
。
然后將求出來的坐標代入到二維前綴和矩陣的計算公式上即可~(但是要注意下標的映射關系)
代碼實現
class Solution {
public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {int row = mat.size(), col = mat[0].size();vector<vector<int>> dp(row+1, vector<int>(col+1));for(int i = 1; i <= row; i++)for(int j = 1; j <= col; j++)dp[i][j] = dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mat[i-1][j-1];vector<vector<int>> answer(row, vector<int>(col));for(int i = 0; i < row; i++)for(int j = 0; j < col; j++){int x1 = max(0, i-k)+1, y1 = max(0, j-k)+1;int x2 = min(row-1, i+k)+1, y2 = min(col-1, j+k)+1;answer[i][j] = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1];}return answer;}
};
?? 寫在最后:以上內容是我在學習以后得一些總結和概括,如有錯誤或者需要補充的地方歡迎各位大佬評論或者私信我交流!!!