leetcode - Insert Interval

来源:互联网 发布:淘宝一千多的硅胶娃娃 编辑:程序博客网 时间:2024/04/29 17:41

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).

You may assume that the intervals were initially sorted according to their start times.

Example 1:
Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9].

Example 2:
Given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16].

This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10].


/** * 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 Solution {public:#define M2    #ifdef M1// O(n2) time, O(1) space, TLE超时    vector<Interval> insert(vector<Interval> &intervals, Interval newInterval) {        vector<Interval>::iterator iter = intervals.begin();        while(iter != intervals.end()) {            if (newInterval.end < iter->start) {                intervals.insert(iter, newInterval);                return intervals;            }            else if (iter->end < newInterval.start) {                ++iter;            }            else {                newInterval.start = min(newInterval.start, iter->start);                newInterval.end   = max(newInterval.end, iter->end);                iter = intervals.erase(iter); // erase will return iterator of next item before iter is removed.            }        }        intervals.insert(intervals.end(), newInterval);        return intervals;    }#endif#ifdef M2/*利用intervals已经排序的特点,采用二分查找,可以在O(lgn)时间内找到起点和终点。这里的最坏情况还是O(n),即要insert的Interval跨头和尾的位置。binarySearch:// 二分查找的变形,将intervals中所有的元素展开(也就是说不分start,end),可以看成一个排序数组。// low,mid,high和经典二分查找类似;// mid为在展开的数组中的下标,而idx为该元素在intervals数组的下标;// mid对应的是intervals[idx]的start还是end呢? 如果mid-2*idx == 1, 则是end,==0,则是start。// 这个函数有点类似于lower_bound, 它的返回值也可以参考lower_bound来理解。[ [1,2] ], 0: 返回0[ [1,5] ], 3: 返回0[ [3,6] ], 7: 返回1[ [1,2] [5,8] ], 0/1/2: 返回0[ [1,2] [5,8] ], 3/4/5/6/7/8: 返回1[ [1,2] [5,8] ], 9: 返回2*/    int binarySearch(vector<Interval> &intervals, int val) {        int low = 0, high = intervals.size()*2-1;        while(low <= high) {            int mid = (low+high)/2; // index in all numbers            int idx = mid/2; // index in intervals array            int midv;            Interval midItem= intervals[idx];                        //mid -2*idx == 1 or 0            midv = (mid-2*idx) ? midItem.end : midItem.start;                        if (midv == val) {                return idx;            }            else if (midv < val) {                low = mid+1;            }            else {                high = mid-1;            }        }                return low/2;    }    vector<Interval> insert(vector<Interval> &intervals, Interval newInterval) {        int pos1 = binarySearch(intervals, newInterval.start);        int pos2 = binarySearch(intervals, newInterval.end);                // newInterval.start is bigger than all numbers in intervals or intervals is empty        // [], [1,2] => [ [1,2] ]        // [ [1,2] ], [3, 4] => [ [1,2] [3,4] ]        if (pos1 == intervals.size()) {            intervals.push_back(newInterval);            return intervals;        }        // if newInterval.end is bigger than all numbers in interval, then it will merge with last item.        // [ [1,3] ], [2, 5] => [ [1,5] ]        if (pos2 == intervals.size()) {            pos2 = intervals.size()-1;        }                // 对于newInterval.start,有两种情况:         // newInterval.start < intervals[pos1].start        // newInterval.start >= intervals[pos1].start        // 不管哪种情况,都需要将newInterval和intervals[pos1]合并。        newInterval.start = min(newInterval.start, intervals[pos1].start);                // 对于newInterval.end, 同样有两情况:        // newInterval.end < intervals[pos2].start        // newInterval.end >= intervals[pos2].start         // 对于第一种情况,我们需要将newInterval和intervals[pos2-1]合并。此时注意要保证pos2-1>=0        // 对于第二种情况,我们将newInterval和intervals[pos2]合并即;        if (newInterval.end < intervals[pos2].start) {            if (pos2-1 >= 0) {                newInterval.end = max(newInterval.end, intervals[pos2-1].end);            }            pos2--;        }        else {            newInterval.end = max(newInterval.end, intervals[pos2].end);        }                // [ [3,5] ], [1,2] => [ [1,2] [3,5] ], pos1=0, pos2=0-1=-1        // [ [3,5] [8,9] ], [6,7] => [ [3,5] [6,7] [8,9] ], pos1=1, pos2=1-1=0        if (pos1 > pos2) {            intervals.insert(next(intervals.begin(), pos1), newInterval);        }        else {            // [ [2,5] ], [3,4] => [ [2,5] ], pos1=0, pos2=0            // [ [1,4] [6,8] ], [2,5] => [ [1,5] [6,8] ], pos1=0, pos2=1-1=0            // [ [1,4] [6,8] ], [2,7] => [ [1,8] ], pos1=0, pos2=1            typedef vector<Interval>::iterator Iterator;            Iterator iter1 = next(intervals.begin(), pos1);            Iterator iter2 = next(intervals.begin(), pos2+1); //[first, last), 所以pos2+1,不然pos2那个位置不会被erase。                        iter1 = intervals.erase(iter1, iter2); // 返回iter2之后的位置,这里返回的iter1已经不等于iter2了,因为iter2已经失效了。            intervals.insert(iter1, newInterval);        }        return intervals;    }#endif};class Solution {public:/*如果用网上的代码,最好的情况是直接插入,不用合并,O(n),最坏的情况是待插入的Interval横跨所有的Intervals,O(n2),因为erase时需要拷贝数组元素,所以会超时。原来自己也写过一个用二分查找的方法,但现在觉得那个代码要判断的条件太多了。这种代码自己平时有时间是可以写出来的,但是需要一些调试才能保证正确性。于是再次思考,觉得应该有更好的写法,因此有了以下代码。这个代码思路清晰,简洁,不容易出错,给别人解释也比较容易。空间复杂度: 这种实现需要O(n)的空间。时间复杂度:构造vector<int>, O(n)binary search: O(logn);insert: 最坏是在头部插入O(n),最好是在尾部插入,O(1),平均是O(n);distance: O(1), 注意insert时如果用list插入效率高,但是distance就得O(n)了;构造vector<Interval> O(n)所以最坏情况也是O(n)的时间复杂度。原理:1. 先用一个int数组保存Interval中的数,由于已经排序,所以可以用binary search。   理解STL中的lower_bound很重要,经典代码不多说。2. 找到合适的插入位置之后,分别插入newInterval的start和end,得到他们插入之后的位置。3. 第一个位置应该在某个Interval的左边,第二个位置则需要在某个Interval的右边,如果不是则需要调整这两个位置。    在处理第二个位置时,对于相同元素要进一步处理:    Input:  [[1,5]], [0,1]    Output: [[0,1],[1,5]]    Expected:   [[0,5]]4. 这两个位置之间的元素都需要去掉,其它的元素保留, 所以只需将有效元素重新填到Interval数组中去即可。*/    // lower_bound 返回大于等于val的第一个位置    template <typename FI, typename T>    FI lower_bound(FI first, FI last, const T &val) {        while(first < last) {            FI mid = next(first, distance(first, last)/2);            if (*mid < val) first = next(mid);            else last = mid;        }        return first;    }    vector<Interval> insert(vector<Interval> &intervals, Interval newInterval) {        vector<int> v2;        for(auto i:intervals) {            v2.push_back(i.start);            v2.push_back(i.end);        }                auto pos1 = lower_bound(v2.begin(), v2.end(), newInterval.start);         pos1 = v2.insert(pos1, newInterval.start); //  插入后返回所插入元素的实际位置        int idx1 = distance(v2.begin(), pos1); // !!! 这里必须先算出idx1,不然在下面插入newInterval.end后pos1可能会失效。                // pos2肯定大于pos1,因为newInterval.end >= newInterval.start        auto pos2 = lower_bound(v2.begin(), v2.end(), newInterval.end);        pos2 = v2.insert(pos2, newInterval.end);        int idx2 = distance(v2.begin(), pos2);                if (idx1%2 == 1) idx1--; // 保证是一个Interval的start        if (idx2%2 == 0) idx2++; // 保证是一个Interval的end                // 进一步合并相同的元素,如[[1,5],插入[0,1]后,[0, 1][1,5] => [0,5]        if (idx2+1 < v2.size() && v2[idx2] == v2[idx2+1]) idx2+=2;                // 将有效元素填入Interval数组中。        vector<Interval> result;        for(int i=0; i<idx1; i+=2) result.push_back({v2[i], v2[i+1]});        result.push_back({v2[idx1], v2[idx2]});        for(int i=idx2+1; i<v2.size(); i+=2) result.push_back({v2[i], v2[i+1]});                return result;    }};


0 0
原创粉丝点击