Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 352. Data Stream as Disjoint Intervals #352

Open
grandyang opened this issue May 30, 2019 · 1 comment
Open

[LeetCode] 352. Data Stream as Disjoint Intervals #352

grandyang opened this issue May 30, 2019 · 1 comment

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

 

Given a data stream input of non-negative integers a1, a2, ..., an, ..., summarize the numbers seen so far as a list of disjoint intervals.

For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, ..., then the summary will be:

[1, 1]
[1, 1], [3, 3]
[1, 1], [3, 3], [7, 7]
[1, 3], [7, 7]
[1, 3], [6, 7]

Follow up:
What if there are lots of merges and the number of disjoint intervals are small compared to the data stream's size?

NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature.

Credits:
Special thanks to @yunhong for adding this problem and creating most of the test cases.

 

这道题说有个数据流每次提供一个数字,然后让我们组成一系列分离的区间,这道题跟之前那道 Insert Interval 很像,思路也很像,每进来一个新的数字 val,都生成一个新的区间 [val, val],并且新建一个空的区间数组 res,用一个变量 cur 来保存要在现有的区间数组中加入新区间的位置。此时遍历现有的区间数组 intervals,对于每一个遍历到的当前区间 interval,假如要加入的区间的结尾位置加1比当前区间的起始位置小,说明二者不相连,将当前区间加入 res。否则当要加入区间的起始位置大于当前位置的结束位置加1,说明二者也没有交集,可以将当前区间加入 res,不过此时 cur 要自增1,因为要加入区间的位置在当前区间的后面。再否则的话,二者就会有交集,需要合并,此时用二者起始位置中较小的更新要加入区间的起始位置,同理,用二者结束位置中较大的去更新要加入区间的结束位置。最终将要加入区间放在 res 中的 cur 位置,然后将 res 赋值给 intervals 即可,参见代码如下:

 

解法一:

class SummaryRanges {
public:
    SummaryRanges() {}
    
    void addNum(int val) {
        vector<int> newInterval{val, val};
        vector<vector<int>> res;
        int cur = 0;
        for (auto interval : intervals) {
            if (newInterval[1] + 1 < interval[0]) {
                res.push_back(interval);
            } else if (newInterval[0] > interval[1] + 1) {
                res.push_back(interval);
                ++cur;
            } else {
                newInterval[0] = min(newInterval[0], interval[0]);
                newInterval[1] = max(newInterval[1], interval[1]);
            }
        }
        res.insert(res.begin() + cur, newInterval);
        intervals = res;
    }
    vector<vector<int>> getIntervals() {
        return intervals;
    }
private:
    vector<vector<int>> intervals;
};

 

感谢热心网友 greentrail 的提醒,我们可以对上面的解法进行优化。由于上面的方法每次添加区间的时候,都要把 res 赋值给 intervals,整个区间数组都要进行拷贝,十分的不高效。这里换一种方式,用一个变量 overlap 来记录所有跟要加入区间有重叠的区间的个数,用变量i表示新区间要加入的位置,这样只要最后 overlap 大于0了,现在 intervals 中将这些重合的区间删掉,然后再将新区间插入,这样就不用进行整体拷贝了,提高了效率,参见代码如下:

 

解法二:

class SummaryRanges {
public:
    SummaryRanges() {}
    
    void addNum(int val) {
        vector<int> newInterval{val, val};
        int i = 0, overlap = 0, n = intervals.size();
        for (; i < n; ++i) {
            if (newInterval[1] + 1 < intervals[i][0]) break; 
            if (newInterval[0] <= intervals[i][1] + 1) {
                newInterval[0] = min(newInterval[0], intervals[i][0]);
                newInterval[1] = max(newInterval[1], intervals[i][1]);
                ++overlap;
            }
        }
        if (overlap > 0) {
            intervals.erase(intervals.begin() + i - overlap, intervals.begin() + i);
        }
        intervals.insert(intervals.begin() + i - overlap, newInterval);
    }
    vector<vector<int>> getIntervals() {
        return intervals;
    }
private:
    vector<vector<int>> intervals;
};

 

Github 同步地址:

#352

 

类似题目:

Insert Interval

Range Module

Find Right Interval 

Summary Ranges

 

参考资料:

https://leetcode.com/problems/data-stream-as-disjoint-intervals/

https://leetcode.com/problems/data-stream-as-disjoint-intervals/discuss/82557/Very-concise-c%2B%2B-solution.

https://leetcode.com/problems/data-stream-as-disjoint-intervals/discuss/82616/C%2B%2B-solution-using-map.-O(logN)-per-adding.

https://leetcode.com/problems/data-stream-as-disjoint-intervals/discuss/82553/Java-solution-using-TreeMap-real-O(logN)-per-adding.

 

LeetCode All in One 题目讲解汇总(持续更新中...)

@RunxinXu
Copy link

RunxinXu commented Nov 4, 2019

Hello, here is my solution to this problem. Because the intervals vector we maintain is sorted, therefore we can search the position where we insert/modify the intervals using dichotomy.

Here's the code.

class SummaryRanges {
public:
    vector<vector<int>> intervals;
    /** Initialize your data structure here. */
    SummaryRanges() {
        
    }   
    
    void addNum(int val) {
        if(intervals.empty()) intervals.push_back(vector<int>({val, val}));
        if(val<intervals[0][0]) 
        {   
            if(val==intervals[0][0]-1) intervals[0][0] = val;
            else intervals.insert(intervals.begin(),vector<int>({val,val}));
        }
        else if(val>intervals.back()[1]) 
        {
            if(val==intervals.back()[1]+1) intervals.back()[1] = val;
            else intervals.insert(intervals.end(),vector<int>({val,val}));
        }
        else if(intervals.back()[0]<=val) return;
        else 
        {
            int left = 0;
            int right = intervals.size()-1;
            while(left<right)
            {
                int mid = left + (right-left)/2;
                if(intervals[mid][0]==val) return;
                else if(intervals[mid][0]>val) right = mid;
                else left = mid+1;
            }
            left--;
            if(val<=intervals[left][1]) return;
            else if(val==intervals[left][1]+1) intervals[left][1] = val;
            else 
            {
                intervals.insert(intervals.begin()+left+1, vector<int>({val,val}));
                left++;
            }

            // 看能不能和后面的连上
            if(left+1<intervals.size())
            {
                if(intervals[left+1][0]==val+1)
                {
                    intervals[left][1] = intervals[left+1][1];
                    intervals.erase(intervals.begin()+left+1);
                }
            }
        }
    }
    
    vector<vector<int>> getIntervals() {
        return intervals;
    }
};

The time/space efficiency:

Solution Runtime Memory
Solution 1 180ms 49.6MB
Solution 2 112ms 26.3MB
My solution 104ms 25.3MB

BTW, your blog is very helpful :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants