博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【leetcode】Insert Interval(hard)★
阅读量:5124 次
发布时间:2019-06-13

本文共 5374 字,大约阅读时间需要 17 分钟。

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].

 

一道看起来不难的题目,却做了很久才AC。主要原因还是思路不清晰导致的。

思路一:直接求解,简单说就是蛮力算法O(N),依次遍历过去,判断是否合并插入。

我自己写了一个直接求解的代码,结果超时了。但是后面看答案,发现很多人都是用的蛮力,却没有超时。我想,可能是跟我用的erase跟insert有关系。

先上大神思路清晰的代码吧:

用新建的vector来存储答案,遇到与新间隔无交叠的就压入,遇到有交叠的就更新新间隔的范围。这样免去了erase和insert的麻烦。

最核心的,通过改变newInterval的范围来解决被覆盖的间隔!!

vector
insert(vector
& intervals, Interval newInterval) { vector
ret; auto it = intervals.begin(); for(; it!=intervals.end(); ++it){ if(newInterval.end < (*it).start) //all intervals after will not overlap with the newInterval break; else if(newInterval.start > (*it).end) //*it will not overlap with the newInterval ret.push_back(*it); else{ //update newInterval bacause *it overlap with the newInterval newInterval.start = min(newInterval.start, (*it).start); newInterval.end = max(newInterval.end, (*it).end); } } // don't forget the rest of the intervals and the newInterval ret.push_back(newInterval); for(; it!=intervals.end(); ++it) ret.push_back(*it); return ret;}

想了想,我那又长又臭的代码还是不放上来了...

 

思路二:二分搜索。我想,既然我自己写得O(n)的代码超时了,那只能想复杂度更少一些的代码了。于是就想用二分搜索来定位新间隔的起始和结束点应该在原间隔的哪个位置。

以上图为例,黑色的是初始间隔,脚标与间隔在vector中的关系是2n, 2n + 1。那么对于newInterval的start和end都有四种可能:

①比最小值还小,返回-1 -1

②比最大值还大,如图就是比16大,返回10 10

③落在某个范围里,如8, 返回  6 7。注意,边界值如1,2,3,5之类的,是算在范围里的。

④落在某个间隙里,如11, 返回 7 8

找到落在的范围后直接把覆盖的范围删除。在交叠的地方,我处理的还是很乱。

这份代码虽然AC了,但我自己并不满意。二分查找很混乱,交叠处理也很混乱,各种混乱!有时间再理一下吧。

注意: 判断奇数偶数时 ((r & 0x01) ==1) 里面的&一定要括起来!

vector
insert2(vector
& intervals, Interval newInterval) { if(intervals.empty()) { intervals.push_back(newInterval); return intervals; } Interval startPos = DividedSearch(intervals, newInterval.start); Interval endPos = DividedSearch(intervals, newInterval.end); if(endPos.end == -1) //新间隔比所有的已有间隔都小 插到最前面 { intervals.insert(intervals.begin(), newInterval); } else if(startPos.start == 2 * intervals.size()) //新间隔比所有的已有间隔都大 插到最后面 { intervals.push_back(newInterval); } else { if(startPos.start == endPos.start && startPos.start & 0x01 == 1) //start与end都落在同一个间隙 { intervals.insert(intervals.begin() + startPos.start / 2 + 1, newInterval); //在该间隙插入 } else if(startPos.start == endPos.start && (startPos.start & 0x01) == 0) //start与end都落在同一个范围 什么都不做 原范围包含了新范围 { } else { //把覆盖的新范围幅值到被覆盖的第一个范围上 intervals[startPos.end / 2].start = (newInterval.start < intervals[startPos.end / 2].start) ? newInterval.start : intervals[startPos.end / 2].start; if(endPos.start == 2 * intervals.size()) { intervals[startPos.end / 2].end = newInterval.end; intervals.erase(intervals.begin() + startPos.end / 2 + 1, intervals.end()); } else { intervals[startPos.end / 2].end = ((endPos.start & 0x01) == 1) ? newInterval.end : intervals[endPos.start / 2].end; //擦除被覆盖的范围 intervals.erase(intervals.begin() + startPos.end / 2 + 1, intervals.begin() + endPos.start / 2 + 1); } } } return intervals; } //二分查找定位新间隔的最大值和最小值落在哪个范围 注意数字可能在两个范围的缝隙中 Interval DividedSearch(vector
intervals, int num) { if(num < intervals[0].start) return Interval(-1, -1); //比最小值小 if(num > intervals.back().end) return Interval(2 * intervals.size(), 2 * intervals.size()); //比最大值大 int l = 0, r = 2 * intervals.size() - 1; Interval range(l, r); while(l < r - 1) { int m = l + (r - l) / 2; int mnum = (m & 0x01 == 1) ? intervals[m / 2].end : intervals[m / 2].start; //m是奇数对应end 是偶数对应start int lnum = (l & 0x01 == 1) ? intervals[l / 2].end : intervals[l / 2].start; int rnum = (r & 0x01 == 1) ? intervals[r / 2].end : intervals[r / 2].start; if(lnum <= num && num <= mnum) { r = m; } else if(mnum <= num && num <= rnum) { l = m; } else { break; } } int lnum = (l & 0x01 == 1) ? intervals[l / 2].end : intervals[l / 2].start; int rnum = (r & 0x01 == 1) ? intervals[r / 2].end : intervals[r / 2].start; if(num == lnum && ((l & 0x01) == 1)) { r = l; l = r - 1; } else if(num == rnum && ((r & 0x0001) == 0)) //注意 (r & 0x0001) 一定要括起来 { l = r; r = l + 1; } return Interval(l, r); }

 

还有,这道题的时间分布很奇怪,最快的居然是python。

转载于:https://www.cnblogs.com/dplearning/p/4513869.html

你可能感兴趣的文章
jenkins Auth fail验证失败
查看>>
django-中间件
查看>>
python使用oracle
查看>>
深入浅出etcd系列 – 心跳和选举
查看>>
SpringAOP aspectJ ProceedingJoinPoint 获取当前方法
查看>>
remove()方法
查看>>
熟悉常用的HDFS操作
查看>>
网络导通概率的研究
查看>>
2019hdu多校1
查看>>
前端性能优化知识,包括css和js
查看>>
微信开发绑定事件实现机制
查看>>
C#递归、动态规划计算斐波那契数列
查看>>
spring的基本用法
查看>>
Windows 8.1 & Windows Phone 开发环境安装遇到的问题
查看>>
jsoup简单的爬取网页数据
查看>>
Content Provider 基础 之URI
查看>>
------------------uniq 去重复
查看>>
mysql中的CURRENT_TIMESTAMP
查看>>
python死磕八之迭代器与生成器
查看>>
oracle索引
查看>>