差分数组的理解

差分数组的理解

差分数组概念

差分数组是一个数组 存放的是相邻数据之间的差值

计算差分数组

	public int[] computeDiff(int[] nums){
		int length = nums.length;
        int[] diff = new int[length];
        for(int i = 0; i < length - 1; ++i){
            diff[i + 1] = nums[i + 1] - nums[i];
        }
    }

比如: 初始的数组为:nums[] = {0, 2, 5, 4, 9, 7, 10 ,0}

img

差分数组用法

差分数组是一个辅助数组 它表示了某个数组的变化 一般用来对数组进行区间修改的操作

这里举一个例子来帮助理解/注意这里的区间指的是下标的区间 不是数值大小的区间

  1. 将区间[1, 4]的数值全部加上3
  2. 将区间[3, 5]的数值全部减去5

暴力计算当然可以 但是如果数据量很大的时候 (比如:\(1e5\)) 经常会超出时间限制

将数组nums中的元素全部加上或者减去某个数组 差分数组不会发生任何变化

  1. 对于区间[1, 4]的所有操作 只会改变两个差分区间这位置的值 : \(nums[1]\)\(nums[5]\)

    1. 因为\(nums[1]\)发生了变化 所以它的\(diff[1] = nums[1] - nums[0]\)也会发生变化
    2. 同理: \(nums[4]\)发生了变化 所以影响了\(nums[5]\)的值

    img

  2. 进行操作[2]

    img

代码实现

	// 假设一共进行 n 次操作
	while(n--){
        // 里面的 left right 和 change 是我们需要用到的操作数
        computeNewDiff(diff, left, right, change);
    }	


    public void computeNewDiff(int[] diff, int left, int right, int change){
        diff[left] += change;
        diff[right + 1] -= change;
    }

获取最终修改后的nums数组的修改值

	for(int i = 1; i <= n; ++i){
        nums[i] = nums[i - 1] + b[i];
    }

利用差分数组解题1674. 使数组互补的最少操作次数

首先我们确定可计算范围 题目中给出的数字的变化范围是:\([1, limit]\) 所以两个数字的的取值范围是:\([2, 2 * limit]\)

然后记录这对数字组的最大值int max = Math.max(nums[i], nums[n - i - 1])和最小值int min = Math.min(nums[i], nums[n - i - 1])同可变化的情况进行分类讨论:

注意这里是怎么构造区间的:我们用两个数字的和作为区间上下界 什么意思呢?

显然我们一次变化的值最小为\(max = 1\)\(min + 1\) 最大为\(min = limit\)\(max + limit\) 所以我们有以下结论

  1. 对于区间\([2, min]\) 这个区间里面的值 我们需要变化两次 \(+2\) 才能够达到目的[缩小\(max 和 min\)]
  2. 对于区间\([min + 1, min + max - 1]\)我们只需要变化二者里面的一个\(+1\)就可以达到目的[增大\(min\)或者缩小\(max\)]
  3. 对于\(min + max\)而言 无需变化
  4. 对于区间\([min + max + 1, max + limit]\) 我们只需要变化\(min\)值即可 \(+1\)
  5. 对于区间\([max + limit + 1, limit + limit]\) 我们需要变化两个值 \(+2\)

所以我们建立一个差值数组int[] diff = new int[limit * 2 + 1];用来记录差值

对于我们引入的每一个数字对 都相当于对这个差值数组做 增加或者减小操作 操作的区间见上方

这样每次我们都能够获取对区间操作之后的差分数组的结果

class Solution {
    public int minMoves(int[] nums, int limit) {
        int n = nums.length;
        int[] delte = new int[2 * limit + 2];
        for(int i = 0; i < n / 2; ++i){
            int low = 1 + Math.min(nums[i], nums[n - i - 1]);
            int high = Math.max(nums[i], nums[n - i - 1]) + limit;
            int sum = nums[i] + nums[n - i - 1];
            delte[low]--;
            delte[sum]--;
            delte[sum + 1]++;
            delte[high + 1]++;
        }
        int ans = n;
        int now = n;
        for(int i : delte){
            now += i;
            ans = Math.min(now, ans);
        }
        return ans;
    }
}

参考博客

差分数组(差分详解+例题)

LeetCode1674题解

posted @ 2020-12-15 23:06  白熊冰啤  阅读(372)  评论(0)    收藏  举报