差分数组菜鸟理解

浅谈差分

当我们只做区间修改和单点查询时,差分这个数据结构就非常有用,不用去写线段树和树状数组。

差分数组是什么?

就是个数组,就是个辅助数组
我数据结构没学好深,谈谈自己对差分的理解
差分就是数据之间的差,就是原始数组相邻元素之间的差值 diff[ i ] = a[ i + 1] - a[ i ]
下面给出一个例子:
在这里插入图片描述
当我们进行区间修改时:

  1. [1 4] 加2
  2. [2 4] 减2
    看着很简单把,我直接暴力循环都能写完,但是当区间达到1e5是就会超时
    这个时候差分数组的优点就体现出来了,其实你对整个区间进行修改他们的差分是不会变的

有了这个想法 我们进行第一步[1 4] 加2 其实diff[ 1 ] 和 diff[ 5 ]变了,diff[ 2, 3, 4 ]是没有变的
在这里插入图片描述
第二步是一样的
在这里插入图片描述
看了这个例子规律就出来了当我们对区间修改时差分数组只有端点的值改变了
也就是我们对区间[ l r ] 改变a也就是
diff[ l ] += a; diff[r + 1] -= a;

while(m--) {//m操作次数
        cin>>l>>r>>change;//l r为端点 change为修改的值
        diff[l] += change;
        diff[r + 1] -= change;
    }

前面还说了差分数组的一个大用处就是单点查询
当我们需要知道改变后的一个值时怎么办
这很简单a[ i ] 就等于diff[0…i]相加
当然也可等于前一个数加上diff[ i ];

int sum = 0;
    for(int i = 0; i < len; ++i) {
        sum += diff[i];
        a[i] = sum;
    }
    for(int i = 0; i < len; ++i) {
        a[i] = a[i - 1] + diff[i];
    }

来一道例题:
https://leetcode-cn.com/problems/minimum-moves-to-make-array-complementary/
1674. 使数组互补的最少操作次数
给你一个长度为 偶数 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](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。
示例 2:

输入:nums = [1,2,2,1], limit = 2
输出:2
解释:经过 2 次操作,你可以将数组 nums 变成 [2,2,2,2] 。你不能将任何数字变更为 3 ,因为 3 > limit 。
示例 3:

输入:nums = [1,2,1,2], limit = 2
输出:0
解释:nums 已经是互补的。

提示:

n == nums.length
2 <= n <= 105
1 <= nums[i] <= limit <= 105
n 是偶数。

是力扣上的一道题:

问我们形成互补数组最少的操作数
我们定义一个cnt[]数组表示cnt[ x ] 互补为x需要的操作数,最后循环cnt数组取最小值便是答案

有了这个思路 关键是我们怎么得出互补为x 需要的操作数
为了方便下面就用 a表示nums[ i ] b表示nums[ len - i - 1]
所以:
如果修改后是 a + b 那么需要的操作为0
如果修改后是 [ 1 + min(a,b), limit + max(a,b)] 那么需要的操作为1
如果修改后是 [2, 2 * limit] 那么需要的操作为2

我们的算法就是跑一遍 len / 2 个组

对 [2, 2 * limit] 区间加2
对[ 1 + min(a,b), limit + max(a,b)] 区间减1
对[a + b, a + b ] 区间减1

这样我们就能的到差分数组了;再使用上面差分数组的方法

class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        int ans = 1e5 + 5;
        int len = nums.size();
        int len2 = len / 2;
        vector<int> cnt(2*limit + 5,0);
        for(int i = 0; i < len2; ++i) {
            int a = nums[i];
            int b = nums[len - i - 1];
            int l = 2;
            int r = 2 * limit;
            cnt[l] += 2;
            cnt[r + 1] -= 2;
            l = 1 + min(a,b);
            r = limit + max(a,b);
            cnt[l] += -1;
            cnt[r + 1] -= -1;
            l = a + b;
            r = a + b;
            cnt[l] += -1;
            cnt[r + 1] -= -1;
        }
        int sum = 0;
        for(int i = 2; i <= 2 * limit; ++i) {
            sum += cnt[i];
            ans = min(ans,sum);
        }
        return ans;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值