随便写写
这道题之前学习数据结构的时候好像就见过
但是印象就不是很深了,如果你直接跑去看答案就会觉得这个题这么简单,但是如果是自己去想的话就可能有各种想法,然后不一定很快能做出来
正文
题目描述
题目分析
这个题目目的是让我们将一系列的区间取并集算法设计及对应的复杂度分析
一开始自己的想法是将区间一个一个的填到一个空集中,这时要考虑的状况非常复杂:
- 首先需要遍历所有的区间,检查新插入的区间起点处在什么位置(通过比较待插入区间起点和已有区间集合的起终点);
- 找到/没找到之后使用相同的方法查找终点位置;
- 此时便可以扩展为一个更大的区间(起点扩展为起点所落在区间的起点,终点扩展为终点所落在区间的终点,如果没有落在某个区间上则无需扩展);
- 最后去掉新扩展产生的区间所覆盖的已有区间。
比如 {【1,3】、【4,5】、【6,8】} 中插入【2,7】,这里首先找到【2,7】的起点2落在【1,3】上;【2,7】的终点7落在【6,8】上;扩展为大区间【1,8】;最后遍历所有现有区间(【1,3】、【4,5】、【6,8】)并去掉其中含在【1,8】当中的,最后将【1,8】加入区间队列。所以合并的结果是【1,8】
显然按照上面的做法太复杂了:对于每个元素的插入需要遍历两次次result中现有区间,复杂度为O(n),之后有需要遍历一次区间去掉所有被覆盖的区间为O(n),总共插入n个元素,所以这一定是O(n^2)复杂度的所以上面的做法不是很好,而且自己写的代码一直有bug就很烦,所以想着有没有更好的办法,最后发现其实只需要稍微改一下就好:
- 我们事先将区间排好序,根据第一个元素大小排序;
- 这样每次新插入的区间只可能和当前区间集合的最后一个区间交叉,所以判断前者的start和后者的end即可(前者的start < 后者的end则表明交叉,需要做下一步,否则直接插入该区间即可);
判断待插入区间的end和当前区间集合最后一个区间的end的大小,取其中大的更新区间集合最后一个区间的末端。
稍微改了一点之后时间复杂度为O(nln(n)) :c++的sort函数使用的快排复杂度为O(nln(n)),然后是对于每一个插入的元素进行一次比较,是O(n),所以最终的时间复杂度为O(nln(n)),有比较大的提升
代码实现
class Solution { public: vector<Interval> merge(vector<Interval>& intervals) { sort(intervals.begin(), intervals.end(), [](Interval a, Interval b){ return (a.start < b.start); } ); vector<Interval> * result = new vector<Interval>(); for (auto i = intervals.begin(); i != intervals.end(); i++) { if (i != intervals.begin() && result->size() >= 1 && i->start <= (result->end() - 1)->end) { (result->end() - 1)->end = ((result->end() - 1)->end) > (i->end) ? (result->end() - 1)->end : i->end; } else { Interval * temp = new Interval(i->start, i->end); result->push_back(*temp); } } return *result; } };
通过结果