1. 程式人生 > 其它 >差分陣列_LeetCode1674

差分陣列_LeetCode1674

技術標籤:演算法筆記演算法leetcode資料結構

差分陣列是什麼

  差分陣列是原陣列相鄰兩數的差值構成的陣列。利用差分陣列,可以快速地實現對區間內所有數同時加(減)去某個數。這是因為差分陣列維護是原陣列的變化量,在每次同時加(減)操作之後,除了差分陣列兩端的值會發生變化,差分陣列內部的值保持不變。引用一位大神部落格中的例子具體說明:

來源:https://note.cser.club/algorithm/untitled

原始陣列和差分陣列

將區間[1,4]中元素同時+3

這時我們就會發現這樣一個規律,當對一個區間進行增減某個值的時候,他的差分陣列對應的區間左端點的值會同步變化,而他的右端點的後一個值則會相反地變化,其實這個很好理解。


題目描述

給你一個長度為 偶數 n 的整數陣列 nums 和一個整數 limit 。每一次操作,你可以將 nums 中的任何整數替換為 1 到 limit 之間的另一個整數。

如果對於所有下標 i(下標從 0 開始),nums[i] + nums[n - 1 - i] 都等於同一個數,則陣列 nums 是 互補的 。例如,陣列 [1,2,3,4] 是互補的,因為對於所有下標 i ,nums[i] + nums[n - 1 - i] = 5 。

返回使陣列互補最少操作次數。

示例 1:

輸入:nums = [1,2,4,3], limit = 4
輸出:1
解釋:經過 1 次操作,你可以將陣列 nums 變成 [1,2,2,3]

提示:

  • n == nums.length
  • 2 <= n <= 1 0 5 10^5 105
  • 1 <= nums[i] <= limit <= 1 0 5 10^5 105
  • n 是偶數。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/minimum-moves-to-make-array-complementary


解題思路

  由已知條件可知,互補的兩數之和S = nums[i]+nums[n-1-i]滿足條件2 ≤ \leq S ≤ \leq 2*limit,因此只需將[2, 2*limit]中的每一個數作為互補和求出對應的操作次數,取最小值即可。

  對於每一對a = nums[i]和b = nums[n-1-i],要是它們互補,需要的操作次數可能為2、1、0。當 a+b == S時,操作次數為0;當 1+min(a,b) <= S <= limit+max(a,b)時,操作次數為1(只需換一個);當 2 <= S <= 2*limit時,操作次數為2(兩個都要換)。

  通過以上分析,可以通過構造差分陣列times[2*limit+2]來完成對區間的同時操作,times[i]在差分陣列的原陣列中表示將i當作互補和時對應的操作次數。具體演算法:遍歷陣列nums,對於每一對a和b,

  1. 將區間[2, 2*limit]的數都+2,對應於2個操作次數,
  2. 將區間[1+min(a,b), limit+max(a,b)]的數都-1,對應於1個操作次數,
  3. 將區間[a+b, a+b]的數都-1,對應於0個操作次數,

最後將差分陣列恢復成原陣列,取最小值即可。演算法的時間複雜度為 O ( n ) O(n) O(n)


程式碼

class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        int n = nums.size(), ans = nums.size(), sum = 0;
		vector<int> times(2*limit+2);	//建立差分陣列,大小2*limit+2
        for(int i = 0; i < n/2; i++){
            int a = nums[i], b = nums[n-1-i];
            //將區間[2, 2*limit]的數都+2
            times[2] += 2;
            //將區間[1+min(a,b), limit+max(a,b)]的數都-1
            times[1+min(a,b)] -= 1;
            times[1+limit+max(a,b)] += 1;
            //將區間[a+b, a+b]的數都-1
            times[a+b] -= 1;
            times[1+a+b] += 1;
        }
        //將差分陣列恢復成原陣列,最小值為答案
		for(int i = 2; i <= 2*limit; i++){
            sum += times[i];
            ans = min(ans, sum);
        }
        return ans;
    }
};

參考

[1] https://note.cser.club/algorithm/untitled

[2] https://leetcode-cn.com/problems/minimum-moves-to-make-array-complementary/solution/jie-zhe-ge-wen-ti-xue-xi-yi-xia-chai-fen-shu-zu-on/