差分數組入門

差分數組

什麼是差分數組?

差分數組:差分數組就是原始數組相鄰元素之間的差。

其實差分數組是一個輔助數組,從側面來表示給定某一數組的變化,一般用來對數組進行區間修改的操作。

比如說對於上文的數組,將區間【1,4】的數值全部加上3,其實當原始數組中元素同時加上或者減掉某個數,那麼他們的差分數組其實是不會變化的,如下圖所示:

對區間 [1, 4] 的元素都進行加3操作,差分數組中改變的只是 d[1] 和 d[5],而 d[2] d[3] d[4] 都沒變。

把上一步得到的數組當成原數組,再將區間【3,5】的數值全部減去5,結果如下:

總結:當對一數組的某個區間進行增減某個值時,其差分數組對應的區間左端點的值會同步變化,而他的右端點的後一個值則會相反變化。

參考博文:差分數組

差分數組的作用

差分數組的作用就是求多次進行區間修改後的數組。構造出差分數組,就可以快速進行區間增減了。花費O(1)的時間修改 diff 數組,就相當於給原數組的整個區間做了修改。

注意 :只能是區間元素同時增加或減少相同的數的情況才能用。

編碼實現

通過原數組求出差分數組:

int[] diff = new int[nums.length];
// 根據原數組構造差分數組
diff[0] = nums[0];
for(int i = 1; i < nums.length; ++i) {
    diff[i] = nums[i] - nums[i - 1];
}

通過差分數組得到結果數組:

int[] res = new int[diff.length];
// 通過差分數組得到結果數組
res[0] = diff[0];
for(int i = 1; i < diff.length; ++i) {
    res[i] = res[i - 1] + diff[i];
}
return res;

//-------------其實直接在diff上操作即可得到結果數組----------------
for(int i = 1; i < diff.length; ++i) {
    diff[i] += diff[i - 1];
}
return diff;

給區間 [i, j] 增加 val(可以是負數):

// 直接操作 diff
diff[i] += val;
if(j + 1 < diff.length) {
    diff[j + 1] -= val;
}

例子

lc-1109. 航班預訂統計
*  給你輸⼊⼀個⻓度為 n 的數組 nums,其中所有元素都是 0。再給你輸⼊⼀個 bookings,⾥⾯是若⼲三元組(i,j,k),
*  每個三元組的含義就是要求你給 nums 數組的閉區間 [i-1,j-1] 中所有元素都加上 k。請你返 回最後的 nums 數組是多少?
輸入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
輸出:[10,55,45,25,25]

這個例子初始的原數組全為0,表示 n 個航班初始預定的座位數都為0。對原數組的某些區間有多次加操作,過程如下:

0	0	0	0	0
10	10					
	20	20
	25	25	25	25
--------------------
10  55  45  25  25
  • 若採用常規思路,則通過二重循環可以得到結果數組,外循環遍歷每個booking,內循環對每個booking中的區間進行遍歷,區間內的值加上座位數,外循環結束,即可得到結果數組。但該解法最壞情況下時間複雜度為O(m*n);

        public int[] corpFlightBookings(int[][] bookings, int n) {
            int[] ans = new int[n];
            for(int[] nums : bookings) {
                for(int i = nums[0]; i <= nums[1]; i++) {
                    ans[i - 1] += nums[2];
                }
            }
            return ans;
        }
    
    執行用時: 1568 ms
    記憶體消耗: 55.3 MB
    
  • 採用差分數組,對原數組的某些區間的加操作,全部都注入到差分數組中,最後直接通過差分數組即可得到結果數組。

        public int[] corpFlightBookings(int[][] bookings, int n) {
            // 初始化差分數組(因為原數組初始全為0)
            int[] diff = new int[n];
            // 遍歷二維數組中的每一行
            for (int[] b : bookings) {
                // 把每一行的值賦給i、j、val。數組下標從0開始,而題中是從1開始,為了對應要-1
                int i = b[0] - 1, j = b[1] - 1, val = b[2];
                // 差分數組左邊界加val(原數組中相當於i後面所有數都加了val)
                diff[i] += val;
                // 隔斷右邊界,把j+1後的所有值減去val,上一步加,這一步減,相當於j+1後的值原數組中沒變
                if(j + 1 < n)
                    diff[j + 1] -= val;
            }
    
            // 上面循環結束,得到的diff就是差分數組,根據差分數組構造結果數組
            int[] res = new int[n];
            res[0] = diff[0];
            for (int i = 1; i < n; i++) {
                // 得到結果數組中的每個值
                res[i] = res[i - 1] + diff[i];
            }
            return res;
        }
    
    執行用時: 2 ms
    記憶體消耗: 55.7 MB
    /*
    差分數組變化:
    	0		0		0		0		0
    	10		0		-10		0		0	// 0 和 1下標加10
    	10		20		-10		-20		0	// 1 和 2下標加20
    	10		45		-10		-20		0	// 1 ~ 4下標加25	
    */
    

    時間複雜度:O(m + n),m 為預定記錄的數量,n 為數組長度;

    空間複雜度:O(n),申請的差分數組diff佔用的空間,其實可以不用申請res,直接把diff當作返回數組,這樣空間可以降到O(1),因為返回值不計入空間複雜度;