AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

LeetCode 352. Data Stream as Disjoint Intervals    原题链接    困难

作者: 作者的头像   yxc ,  2018-07-02 01:17:16 ,  所有人可见 ,  阅读 2380


10


4

题目描述

给出一个非负整数的数据流 $a_1, a_2, … a_n, … $,请将它们动态地维护成一系列不相交的区间。

思考题: 假设有非常多的区间合并操作,并且区间总数远小于所有数的个数时,该怎么做?

样例

给定数据流:1, 3, 7, 2, 6, ..., 动态得到的区间是:
[1, 1]
[1, 1], [3, 3]
[1, 1], [3, 3], [7, 7]
[1, 3], [7, 7]
[1, 3], [6, 7]


算法

(平衡树) addNum: $O(logn)$, getIntervals: $O(n)$

我们用 map<int,int> L, R 来动态维护所有区间,L可以从区间右端点索引到左端点,R可以从区间左端点索引到右端点。
举例来说,假设有个区间是[x, y],则L[y] = x并且R[x] = y。

对于addNum(val)操作:

  1. 我们先判断val是否已经包含在某个区间中。具体来说,先用upper_bound(val)找出最后一个左端点小于等于val的区间,然后判断该区间的右端点是否大于等于val。
  2. 如果val不包含在任何区间中,则分别判断val-1和val+1是否存在:
    • 如果都存在,则合并左右区间,合并方式:R[L[val - 1]] = R[val + 1],L[R[val + 1]] = L[val - 1],然后将R[val+1]和L[val-1]删除;
    • 如果只有一边存在,则将val插入该区间中;
    • 如果都不存在,则将val表示成一个单独的区间;

对于getIntervals()操作,直接输出所有区间即可。

时间复杂度分析:对于addNum操作,只涉及常数次平衡树的增删改查操作,所以时间复杂度是 $O(logn)$。对于getIntervals操作,需要将整棵平衡树遍历一遍,所以时间复杂度是 $O(n)$。

C++ 代码

/**
 * Definition for an interval.
 * struct Interval {
 *     int start;
 *     int end;
 *     Interval() : start(0), end(0) {}
 *     Interval(int s, int e) : start(s), end(e) {}
 * };
 */
class SummaryRanges {
public:
    /** Initialize your data structure here. */
    map<int,int>L, R;
    SummaryRanges() {

    }

    void addNum(int val) {
        if (R.size())
        {
            auto it = R.upper_bound(val);
            if (it != R.begin())
            {
                -- it;
                if (it->second >= val) return;
            }
        }
        int right = R.count(val + 1), left = L.count(val - 1);
        if (left && right)
        {
            R[L[val - 1]] = R[val + 1];
            L[R[val + 1]] = L[val - 1];
            R.erase(val + 1), L.erase(val - 1);
        }
        else if (right)
        {
            L[R[val + 1]] = val;
            R[val] = R[val + 1];
            R.erase(val + 1);
        }
        else if (left)
        {
            R[L[val - 1]] = val;
            L[val] = L[val - 1];
            L.erase(val - 1);
        }
        else
        {
            R[val] = val;
            L[val] = val;
        }
    }

    vector<Interval> getIntervals() {
        vector<Interval> res;
        for (auto &p : R) res.push_back(Interval(p.first, p.second));
        return res;
    }
};

/**
 * Your SummaryRanges object will be instantiated and called as such:
 * SummaryRanges obj = new SummaryRanges();
 * obj.addNum(val);
 * vector<Interval> param_2 = obj.getIntervals();
 */

12 评论


用户头像
KillLemon   2021-01-13 10:11         踩      回复

写注释

class SummaryRanges {
public:
    /** Initialize your data structure here. */
    map<int,int> L,R;
    SummaryRanges() {

    }

    void addNum(int val) {
        if(R.size()){
            //upper_bound返回的是大于某个数的最小元素,这里是要找出小于等于 val 的最大元素,因此--
            auto it = R.upper_bound(val);   
            if(it != R.begin()){
                it --;
                if(it->second >= val) return;   //如果当前区间右端点>=val说明点在区间内,返回即可
            }
        }

        //统计左端点为val - 1和右端点为val + 1的区间是否存在,注意.count(key)是判断对应key节点出现次数
        //这里需要体会一下左右端点的变化
        int left = L.count(val - 1),right = R.count(val + 1);
        //如果val - 1,val + 1区间都存在,进行合并,这里也有点绕,需要想一想
        if(left && right){
            R[L[val - 1]] = R[val + 1];
            L[R[val + 1]] = L[val - 1];
            R.erase(val + 1),L.erase(val - 1);
        }
        else if(right){
            L[R[val + 1]] = val;
            R[val] = R[val + 1];
            R.erase(val + 1);
        }
        else if(left){
            R[L[val - 1]] = val;
            L[val] = L[val - 1];
            L.erase(val - 1);
        }
        else{
            R[val] = L[val] = val;
        }
    }

    vector<vector<int>> getIntervals() {
        vector<vector<int>> res;
        for(auto p : R) res.push_back({p.first,p.second});
        return res;
    }
};

/**
 * Your SummaryRanges object will be instantiated and called as such:
 * SummaryRanges* obj = new SummaryRanges();
 * obj->addNum(val);
 * vector<vector<int>> param_2 = obj->getIntervals();
 */

用户头像
Lemmon_kk   2020-04-11 22:33         踩      回复

我记得好像还有一个题和这个很像,y总当时是用hash写的128,这两个做法应该都一样吧。

用户头像
yxc   2020-04-21 03:12         踩      回复

也是可以的。


用户头像
一只菜鸡   2019-08-27 02:20         踩      回复

为什么addNum()里第一种val已经在某个区间的情况写成这样就通不过,求大佬解答
auto it = R.upper_bound(val);
if (it != R.end() && it != R.begin()) {
it–;
if (it->second >= val)
return;
}

用户头像
一只菜鸡   2019-08-27 04:31         踩      回复

明白了,这里并不需要R中存在真正的val的upper_bound, 只需要这个iterator前一个区间的右端点不小于val就可以了,如果用lower_bound就没有这种困惑了

用户头像
一只菜鸡   2019-08-27 04:37    回复了 一只菜鸡 的评论         踩      回复

这里用lower_bound还是upper_bound都可以把if (R.size()) 这个判断去掉

用户头像
yxc   2019-08-27 11:13    回复了 一只菜鸡 的评论         踩      回复

对滴

用户头像
yxc   2019-08-27 11:14    回复了 一只菜鸡 的评论         踩      回复

if (it != R.end())这句话起到了和if (R.size())这句话同样的作用,所以可以去掉hh


用户头像
jiashihong   2019-08-26 17:56         踩      回复

– it; 这个为什么要减一呢,视频里用lower_bound为什么就不用减啊。。

用户头像
yxc   2019-08-26 18:16         踩      回复

upper_bound返回的是大于某个数的最小元素,这里是要找出小于等于 val 的最大元素,因此可以先找出大于val的最小元素it,那么--it之后就是小于等于val的最大元素了。

用户头像
jiashihong   2019-08-26 19:01    回复了 yxc 的评论         踩      回复

啊!我知道了,我把L和R索引看反了!!!我说怎么看都不对呢

用户头像
yxc   2019-08-26 19:30    回复了 jiashihong 的评论         踩      回复

哈哈是的,昨天讲课的时候突然发现如果用L判断,就不需要 it--了,所以就改用 L 了。


App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息