内容简介:今天是这道题的题意也很简单,只有一句话:“Given a collection of intervals, merge all overlapping intervals.”interval是
今天是 LeetCode专题的第33篇 文章,我们一起来看LeetCode的第56题,它的难度是Medium。
题意
这道题的题意也很简单,只有一句话:“Given a collection of intervals, merge all overlapping intervals.”
interval是 间隔、区间 的意思,也就是说题目会给我们一系列区间,让我们把这些区间合并在一起。
我们看下题目给的样例来感受一下:
“
Input: [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
“
Input: [[1,4],[4,5]] Output: [[1,5]] Explanation: Intervals [1,4] and [4,5] are considered overlapping.
分析
通过观察样例,我们发现题目通过数组给定区间,每个区间有两个端点。两个区间能够合并的条件,就是 互相之间有交叉的部分 。我们看下下图,这应该很直观。
当两个区间[s1, e1]和[s2, e2]中的e1 >= s2时,这两个区间就可以进行合并。合并之后得到的新区间是[s1, e2]。
但是这存在一个小问题,我们如何能判断第一个区间一定在第二个区间的左侧呢,会不会发生重叠呢?
如果是这种情况那么合并之后的结果就是[s2, e2]了,另外一个问题是,这样的区间一共有N个,我们 怎么判断合并的顺序 呢?很有可能出现AB两个区间原本不能合并,但是A合并了区间C之后又可以和B合并的情况。如果我们枚举的话会很麻烦,我们不但需要考虑合并的时候会发生的种种情况,还需要考虑合并的发生顺序。而且我们也很难得知是否所有能够合并的区间已经合并完成。
题解
我们梳理一下目前遇到的问题,第一个问题是区间根据位置的不同 合并之后的结果可能有多个 。第二个问题是 区间合并之后会创建新的合并的可能 ,第三个问题是我们 判断当前是否还有合并的可能开销很大 。
其中第三个问题是前两个问题导致的,只要解决了其中一个,第三个问题自然迎刃而解。其中 第二个问题是无法解决 的,因为这是区间合并的天然属性,我们执行区间合并必然会有这样的情况发生。所以我们只能针对第一个问题下手,合并之后的结果可能有多种的本质原因是区间的位置关系可能有多个。A和B合并,A可以出现在B的左侧,也可以出现在B的右侧。再根据区间长短关系,所以才导致了多种结果。
如果我们能够保证 A一定出现在B的左侧 ,那么A和B就只有三种情况。一种是A和B不相交,也就是不能合并。第二种是A和B相交,第三种是A包含B。
我们把图画出来,很容易发现后面两种能够合并的情况其实可以概括成一种,它们合并之后的结果都是[s1, max(e1, e2)]。
既然如此,本题的关键就是 区间之间的顺序 。假如我们保证了区间的顺序,我们依次合并显然可以很容易得到结果。但是我们怎么得到区间顺序呢?
其实很简单,就是 排序 ,既然区间本来没有顺序,我们对它们排序,不就有顺序了吗?
我们可以规定左侧端点小的区间排在前面,如果两个区间左端点一致,右端点小的靠前。这是什么?这其实就是 字典序排序 ,在 Python 当中我们可以直接调用sorted对多元素的list进行字典序排序。如果是其他语言,也不麻烦,我们可以自己定义比较算子,一样可以解决。
既然区间有序了,我们只需要从左到右遍历就可以覆盖所有情况了,第三个问题也就解决了。
最后,我们来看下代码,只要想到了排序,这道题并不难。但是初学者往往很难想到 排序 上,这当中的思维推导过程才是我们需要熟悉的。
class Solution: def merge(self, intervals: List[List[int]]) -> List[List[int]]: ret = [] if len(intervals) == 0: return ret # 字典序排序 intervals = sorted(intervals) # l,r表示当前区间的两个端点 l, r = intervals[0] # 从1开始遍历区间,进行合并 for i in range(1, len(intervals)): s, e = intervals[i] # 如果可以合并,维护合并之后的右端点 if s <= r: r = max(r, e) else: # 否则加入答案,将i区间作为当前进行合并的区间 ret.append([l, r]) l, r = s, e
ret.append([l, r]) <span>return</span> ret
结尾
到这里,这道题就结束了,除了排序之外,这道题还可以使用 连通分量 的思想来做。我们可以枚举所有区间的关系,我们可以把所有区间看成是图上的一个点。只要两个区间可以合并,我们就把它们两个点之间连一条边。这样所有可以合并在一起的区间,就构成了一个连通分量。我们最后遍历这整张图上所有的连通分量就可以拿到所有合并之后的区间结果。和排序比起来,这个算法显然复杂得多,需要 建图、连边,然后计算连通分量 。并且复杂度也很高,是 的算法。我觉得完全没有必要,所以就不多介绍了,感兴趣的同学可以自己了解一下,只要图建好之后,连通分量的求解也不是很复杂。
你看,复杂的算法未必效果就好,还是要适合题目的才是最好的,但什么算法是适合题目的呢?这才是所有问题的关键。这不禁让我想到了电影霍元甲里面,李连杰说的一句话,武功本身没有强弱之别,只是使用武功的武者实力有高下之分。算法也是一样,复杂的算法并不一定就强,灵活运用、理性分析、合理运用才是王道。
今天的文章就到这里,原创不易,需要你 一个关注 的支持,你的举手之劳对我来说很重要。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 一款IP区间合并工具及如何使用Python实现相同功能
- 线段树(区间树)
- 模型预测与置信区间
- 快速区间查询算法 - 线段树
- 树形结构数据存储方案(五):区间嵌套
- 【LeetCode】贪心算法--划分字母区间(763)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
松本行弘的程式世界
松本行弘 / 鄧瑋敦 / 博碩 / 2010年07月27日
讓Ruby之父教您大師級的程式思考術! 本書以松本行弘先生對程式本質的深層認知、各種技術之優缺點的掌握,闡述Ruby這套程式語言的設計理念,並由此延伸讓您一窺程式設計的奧妙之處。本書內含許多以Ruby、Lisp、Smalltalk、Erlang、JavaScript等動態語言所寫成的範例,從動態語言、函數式程式設計等領域開展您的學習視野。 本書精華: ‧物件導向與抽象化 ‧......一起来看看 《松本行弘的程式世界》 这本书的介绍吧!