字首和
一維字首和
字首和陣列 sum 的每一位記錄的是當前位置距離起點位置,這連續一段的和區間和。
利用字首和陣列,我們可以快速得到陣列任意區間的元素和。
構造字首和陣列的時間複雜度是O(n)
,獲得區間和的複雜度是O(1)
當nums陣列的元素下標從0開始算時,需要做出一些調整
模板和例題
給定一個整數陣列 nums,求出陣列從索引 i 到 j(i ≤ j)範圍內元素的總和,包含 i、j 兩點。
實現 NumArray 類:
NumArray(int[] nums) 使用陣列 nums 初始化物件
int sumRange(int i, int j) 返回陣列 nums 從索引 i 到 j(i ≤ j)範圍內元素的總和,包含 i、j 兩點(也就是 sum(nums[i], nums[i + 1], ... , nums[j]))
示例:
輸入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
輸出:
[null, 1, -1, -3]
解釋:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1))
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
提示:
0 <= nums.length <= 104
-105 <= nums[i] <= 105
0 <= i <= j < nums.length
最多呼叫 104 次 sumRange 方法
class NumArray {
int[] sum;
public NumArray(int[] nums) {
int n = nums.length;
// 字首和陣列下標從 1 開始,因此設定長度為 n + 1(模板部分)
sum = new int[n + 1];
// 預處理除字首和陣列(模板部分)
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + nums[i - 1];
}
public int sumRange(int i, int j) {
// 求某一段區域和 [i, j] 的模板是 sum[j] - sum[i - 1](模板部分)
// 但由於我們原陣列下標從 0 開始,因此要在模板的基礎上進行 + 1
i++; j++;
return sum[j] - sum[i - 1];
}
}
二維字首和
用來解決二維矩陣中的矩形區域求和問題
sum矩陣中每一個數(x, y)表示(0, 0)為左上角到(x, y)為右下角的矩形數值總和
模板和例題
// 預處理字首和陣列
{
sum = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 當前格子(和) = 上方的格子(和) + 左邊的格子(和) - 左上角的格子(和) + 當前格子(值)【和是指對應的字首和,值是指原陣列中的值】
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + matrix[i - 1][j - 1];
}
}
}
// 首先我們要令左上角為 (x1, y1) 右下角為 (x2, y2)
// 計算 (x1, y1, x2, y2) 的結果
{
// 字首和是從 1 開始,原陣列是從 0 開始,上來先將原陣列座標全部 +1,轉換為字首和座標
x1++; y1++; x2++; y2++;
// 記作 22 - 12 - 21 + 11,然後 不減,減第一位,減第二位,減兩位
// 也可以記作 22 - 12(x - 1) - 21(y - 1) + 11(x y 都 - 1)
ans = sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
給定一個二維矩陣 matrix,以下型別的多個請求:
計算其子矩形範圍內元素的總和,該子矩陣的左上角為 (row1, col1) ,右下角為 (row2, col2) 。
實現 NumMatrix 類:
NumMatrix(int[][] matrix) 給定整數矩陣 matrix 進行初始化
int sumRegion(int row1, int col1, int row2, int col2) 返回左上角 (row1, col1) 、右下角 (row2, col2) 的子矩陣的元素總和。
示例 1:
輸入:
["NumMatrix","sumRegion","sumRegion","sumRegion"]
[[[[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]]],[2,1,4,3],[1,1,2,2],[1,2,2,4]]
輸出:
[null, 8, 11, 12]
解釋:
NumMatrix numMatrix = new NumMatrix([[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]]]);
numMatrix.sumRegion(2, 1, 4, 3); // return 8 (紅色矩形框的元素總和)
numMatrix.sumRegion(1, 1, 2, 2); // return 11 (綠色矩形框的元素總和)
numMatrix.sumRegion(1, 2, 2, 4); // return 12 (藍色矩形框的元素總和)
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 200
-105 <= matrix[i][j] <= 105
0 <= row1 <= row2 < m
0 <= col1 <= col2 < n
最多呼叫 104 次 sumRegion 方法
class NumMatrix {
int[][] sum;
public NumMatrix(int[][] matrix) {
int n = matrix.length, m = n == 0 ? 0 : matrix[0].length;
// 與「一維字首和」一樣,字首和陣列下標從 1 開始,因此設定矩陣形狀為 [n + 1][m + 1](模板部分)
sum = new int[n + 1][m + 1];
// 預處理除字首和陣列(模板部分)
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + matrix[i - 1][j - 1];
}
}
}
public int sumRegion(int x1, int y1, int x2, int y2) {
// 求某一段區域和 [i, j] 的模板是 sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];(模板部分)
// 但由於我們源陣列下標從 0 開始,因此要在模板的基礎上進行 + 1
x1++; y1++; x2++; y2++;
return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
}
差分
一維差分
「差分」是求「字首和」的逆向過程。
但是差分陣列的構造過程並不重要,利用差分的性質實現區間修改和單點查詢是重點
差分陣列的求法:c[i] = nums[i] - nums[i-1]
,原陣列中相鄰元素的差值組成的陣列就是差分陣列
差分陣列對應的概念是字首和陣列,差分陣列的第 i
個數即為原陣列的第 i-1
個元素和第 i
個元素的差值,也就是說我們對差分陣列求字首和即可得到原陣列。
差分陣列的性質是,當我們希望對原陣列的某一個區間 [l,r]
施加一個增量inc
時,差分陣列 d
對應的改變是:d[l]
增加 inc
,d[r+1]
減少 inc
。這樣對於區間的修改就變為了對於兩個位置的修改。並且這種修改是可以疊加的,即當我們多次對原陣列的不同區間施加不同的增量,我們只要按規則修改差分陣列即可。
當對一個區間進行增減某個值的時候,他的差分陣列對應的區間左端點的值會同步變化,而他的右端點的後一個值則會相反地變化
模板和例題
這裡有 n 個航班,它們分別從 1 到 n 進行編號。
有一份航班預訂表 bookings ,表中第 i 條預訂記錄 bookings[i] = [firsti, lasti, seatsi] 意味著在從 firsti 到 lasti (包含 firsti 和 lasti )的 每個航班 上預訂了 seatsi 個座位。
請你返回一個長度為 n 的陣列 answer,其中 answer[i] 是航班 i 上預訂的座位總數。
示例 1:
輸入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
輸出:[10,55,45,25,25]
解釋:
航班編號 1 2 3 4 5
預訂記錄 1 : 10 10
預訂記錄 2 : 20 20
預訂記錄 3 : 25 25 25 25
總座位數: 10 55 45 25 25
因此,answer = [10,55,45,25,25]
示例 2:
輸入:bookings = [[1,2,10],[2,2,15]], n = 2
輸出:[10,25]
解釋:
航班編號 1 2
預訂記錄 1 : 10 10
預訂記錄 2 : 15
總座位數: 10 25
因此,answer = [10,25]
提示:
1 <= n <= 2 * 104
1 <= bookings.length <= 2 * 104
bookings[i].length == 3
1 <= firsti <= lasti <= n
1 <= seatsi <= 104
class Solution {
public int[] corpFlightBookings(int[][] bs, int n) {
int[] c = new int[n + 1];
for (int[] bo : bs) {
int l = bo[0] - 1, r = bo[1] - 1, v = bo[2];
c[l] += v;
c[r + 1] -= v;
}
int[] ans = new int[n];
ans[0] = c[0];
for (int i = 1; i < n; i++) {
ans[i] = ans[i - 1] + c[i];
}
return ans;
}
}
二維差分
differ[x1][y1] += c; //原矩陣(x1,y1)到右下角的值都加c
differ[x2 + 1][y1] -= c; //原矩陣(x2+1,y1)到右下角的值都加c
differ[x1][y2 + 1] -= c; //原矩陣(x1,y2+1)到右下角的值都加c
differ[x2 + 1][y2 + 1] += c; //原矩陣(x2+1,y2+1)到右下角多減去了c,再加回來
模板和例題
給定一個二維矩陣 matrix,以下請求:
令子矩形範圍內每個元素的加或減一個常數c,該子矩陣的左上角為 (row1, col1) ,右下角為 (row2, col2) 。
依次輸入二維矩陣, (row1, col1) ,(row2, col2) ,c
並返回處理後的二維矩陣
示例1:
輸入:matrix = [[1,2,3],[4,5,6],[7,8,9]],1,1,2,2,1
輸出:[[1,2,3],[4,6,7],[7,9,10]]
public class demo1 {
static int[][] differ; //二維差分陣列
static int m, n;
public static void main(String[] args) {
int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int[][] ans = diff(matrix, 1, 1, 2, 2, 1);
System.out.println(Arrays.deepToString(ans));
}
public static void insert(int x1, int y1, int x2, int y2, int c) { //差分陣列的構造
differ[x1][y1] += c;
differ[x2 + 1][y1] -= c;
differ[x1][y2 + 1] -= c;
differ[x2 + 1][y2 + 1] += c;
}
public static int[][] diff(int[][] matrix, int x1, int y1, int x2, int y2, int c) {
m = matrix.length;
n = m == 0 ? 0 : matrix[0].length;
differ = new int[m + 2][n + 2];
//所給matrix下標都從0開始,而差分陣列的下標必須從1,開始,所以下標做一定的調整
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
insert(i + 1, j + 1, i + 1, j + 1, matrix[i][j]);
}
}
insert(x1 + 1, y1 + 1, x2 + 1, y2 + 1, c); //二維差分矩陣
//求二維字首和
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
differ[i][j] = differ[i - 1][j] + differ[i][j - 1] - differ[i - 1][j - 1] + differ[i][j];
}
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
matrix[i - 1][j - 1] = differ[i][j];
}
}
return matrix;
}
}
//輸出:[[1, 2, 3], [4, 6, 7], [7, 9, 10]]