leetcode——【差分数组】

差分数组
  • 概念
    差分数组是与前缀和数组所对应的一种逆操作,类似于求导和积分,也就是说,对差分数组求前缀和,可以得到原数组,同样的,对前缀和数组求差分,也可以得到原数组。
  • 性质
    当我们希望对原数组的某一区间[l, r]施加一个增量inc时,查分数组d对应的变化是:d[l]增加inc,d[r+1]减少inc,并且这种操作是可叠加的。

当我们需要对原数组的不同区间施加不同的增量,我们只要按规则修改差分数组即可。

例题
  • 航班预订问题
    在这里插入图片描述
思路

对于区间改变问题,可采用此种方法,对区间起始位置采用加操作,区间结束位置+1采用减操作。

实现
class Solution {
public:
    vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
        vector<int> nums(n);
        for (auto& booking : bookings) {
            nums[booking[0] - 1] += booking[2];
            if (booking[1] < n) {
                nums[booking[1]] -= booking[2];
            }
        }
        for (int i = 1; i < n; i++) {
            nums[i] += nums[i - 1];
        }
        return nums;
    }
};
  • 日程安排表问题
    在这里插入图片描述
思路

因为此处时间跨度较大,采用map的结构维护差分数组(因为其对key自动排序的缘故),在维护差分数组后并进行遍历比较一次,得到此刻最大的预定次数。

实现
class MyCalendarThree {
public:
    MyCalendarThree() {

    }
    
    int book(int start, int end) {
        dec_map[start]++;
        dec_map[end]--;
        int cnt = 0;
        int ret = 0;
        for(auto dec : dec_map){
            cnt += dec.second;
            ret = max(ret, cnt);
        }
        return ret;
    }

private:
    map<int, int> dec_map;
};

在这里插入图片描述

思路

先去试着添加,然后检查添加后是否符合要求,若不符合,退回添加。

实现
class MyCalendarTwo {
public:
    MyCalendarTwo() {

    }
    
    bool book(int start, int end) {
        dec_map[start]++;
        dec_map[end]--;
        int cnt = 0;
        for(auto dec : dec_map){
            cnt += dec.second;
            if (cnt >= 3){
                dec_map[start]--;
                dec_map[end]++;
                return false;
            }
        }
        return true;
    }

private:
    map<int, int> dec_map;
};

在这里插入图片描述

思路

采用上述方法或者使用map构建,lower_bound进行快速的二分查找。

实现
class MyCalendar {
public:
    map<int, int> pool;
    MyCalendar() {}
    
    bool book(int start, int end) {
        // lower_bound()返回值是一个迭代器,返回指向>=end的第一个值的位置
        map<int, int>::iterator it = pool.lower_bound(end);
        // 判断是否为第一个元素,或前一个元素的结束时间<= start
        if (it == pool.begin() || (--it)->second <= start) {
            pool[start] = end;
            return true;
        }
        return false;
    }
};
  • 使数组互补最少的操作次数
    在这里插入图片描述
思路

显然,如果sum <= min(a, b),a和b均需要变化
如果a+b > sum > min(a, b),a和b需要变化较大的那一个
如果sum > max(a, b) + limit, a和b需要全部变化
如果a + b < sum <= max(a, b) + limit,a和b需要变化较小的那一个
可用差分数组模拟该过程,diff[i]表示sum为i时需要变化多少次

实现
class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        int len = nums.size();
        vector<int> diff(limit << 1 + 1);
        for(int i = 0; i < len / 2; ++i){
            int first = nums[i];
            int second = nums[len - i - 1];
            diff[2] += 2;
            diff[min(first, second) + 1] -= 1;
            diff[first + second] -= 1;
            diff[first + second + 1] += 1;
            diff[max(first, second) + limit + 1] += 1;
        }
        int ret = diff[2]; 
        int cnt = diff[2];
        for(int i = 3; i < limit << 1 + 1; ++i){
            diff[i] += diff[i - 1];
            ret = min(ret, diff[i]);
        }
        return ret;
    }
};
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值