leetcode——數組算法——前綴和構建和應用
前綴和技巧適用於快速、頻繁地計算一個索引區間內的元素之和
303. 區域和檢索 - 數組不可變
比如leetcode 303. 區域和(檢索 - 數組不可變)
題目介紹:
給定一個整數數組 nums,處理以下類型的多個查詢:
- 計算索引
left和right(包含left和right)之間的nums元素的 和 ,其中left <= right
實現 NumArray 類:
NumArray(int[] nums)使用數組nums初始化對象int sumRange(int i, int j)返回數組nums中索引left和right之間的元素的 總和 ,包含left和right兩點(也就是nums[left] + nums[left + 1] + ... + nums[right])
示例 1:
輸入:
["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))
解法一:
1.在sumRange裏面,for循環從left到right遍歷nums,用一個變量記錄。
代碼如下:
class NumArray {
//類裏肯定有一個int[]成員
private int[] myArray;
public NumArray(int[] nums) {
this.myArray=nums;
}
public int sumRange(int left, int right) {
int result=0;
if(left>right||left<0||right>myArray.length){
return 0;
}
//myArray[left]一直加到myArray[right]
for(int i=left;i<=right;i++){
result+=myArray[i];
}
return result;
}
}
如果多次調用sumRange,會一直重複計算。
解法2
2.在構造函數中,構造一個關於nums的前綴和數組preNums,preNums[i]的值就是nums前i項的和。
Q:如何構造這個前綴和數組?
A:前綴和數組的每一項 = 前一項(前i-1項的和)+ nums[i]。
注意:因為前綴和數組的表達意義應該是前1項的和,前2項的和;而沒有個前0項的和。
所以這裏將preNum[0]=0;目的是更符合我們的表達語義。
比如preNum[1]就是nums前1項的和。
代碼如下:
class NumArray {
public int[] getPreArray() {
return preArray;
}
//記錄一個前綴和數組,避免sumRange重複的for
private int[] preArray;
public NumArray(int[] nums) {
preArray = new int[nums.length + 1];
// 計算 nums 的累加和
for (int i = 1; i < preArray.length; i++) {
preArray[i] = preArray[i - 1] + nums[i - 1];
}
}
public int sumRange(int left, int right) {
int result=preArray[right+1]-preArray[left];
return result;
}
}
304. 二維區域和檢索 - 矩陣不可變
如果是二維數組的前綴和如何構建和使用呢?
比如leetcode 304. 二維區域和檢索 - 矩陣不可變
給定一個二維矩陣 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 (藍色矩形框的元素總和)
如果本題繼續雙for循環,開銷很大,如果sumRegion使用頻繁,則可以使用一個前綴和數組存儲NumMatrix前i行前j列的和。
核心
Q:二維數組的前綴和如何構建呢?
A:行列的length各+1,然後找規律:左面的+上面的+自己-左對角線的
Q:規律怎麼找的?
A:比如上圖中的matrix(2)(2),它值為0;現在要計算前3行前3列的前綴和。
注意它左邊的2和上面的3,如果讓他倆各自位置的前綴和相加,然後再減去對角線的6位置的前綴和,就是0位置的前綴和。
如下圖所示(可以好好屢屢):
代碼如下:
class NumMatrix {
public int[][] getPreMatrix() {
return preMatrix;
}
private int[][] preMatrix;
public NumMatrix(int[][] matrix) {
preMatrix=new int[matrix.length+1][matrix[0].length+1];
//構建 二維前綴和數組
for(int i=1;i< preMatrix.length;i++){
for (int j = 1; j < preMatrix[0].length; j++) {
//找規律
//左面的+上面的+自己-左對角線的
preMatrix[i][j]=preMatrix[i][j-1]+preMatrix[i-1][j]+matrix[i-1][j-1]-preMatrix[i-1][j-1];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return preMatrix[row2+1][col2+1] - preMatrix[row1][col2+1] - preMatrix[row2+1][col1] + preMatrix[row1][col1];
}
public static void main(String[] args) {
int[][] 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}
};
NumMatrix numMatrix = new NumMatrix(matrix);
System.out.println(Arrays.deepToString(numMatrix.getPreMatrix()));
System.out.println(numMatrix.sumRegion(2, 1, 4, 3));
}
}