问题描述
有两个有序的集合,集合中的每个元素都是一段范围,求其交集。例如集合{[4, 8],[9, 13]}和{[6, 12]}的交集是{[6, 8],[9, 12]}.
建立一个集合类
首先我们要设计一个集合类用以表示上面的集合(集合里的元素也就是通常意义下的区间).因此,建立一个集合类MySet,它包含两个属性,也就是区间的两个端点.我们分别记作mins和maxs.建立一个这样的集合是容易的.
class MySet:
def __init__(self, mins, maxs):
self.mins = mins
self.maxs = maxs
设计获得端点的方法
因为求集合的交集涉及到区间端点大小的比较,所以我们需要设计两个获得端点的方法,这两个方法非常简单,只是返回当前对象的mins和maxs.
def getMin(self):
return self.mins
def getMax(self):
return self.maxs
分析
有了这个类以后,我们就有了可以进行操作的对象,接下来就是分析这个算法的设计思路了.
如图,为了直观显示,我用彩色的矩形代替线段(线段表示的就是这里的有序集合,所谓有序指的是在这集合里的区间一定是按照它们在数组里的下标顺序递增的).设有区间[a, b]和[c, d].它们之间的关系无非有以下几种:
图1
可以想象成两辆公交车慢慢靠近并最终背道而驰的情景.根据这两个区间长度大小有两种情况,在第三时刻又会造成两种不同的结果.按照这种情景,则共有6中不一样的情况,在接下来的算法设计中,我们就得同时考虑到这6中不一样的情况.
利用两个数组来存储这些区间,则问题转化成了这两个数组之间的元素进行某种比较,获得交集.假设这两个数组分别为l1和l2,如果l1和l2分别有一个区间,则l1和l2至多有一个交集;如果l1有1个区间和l2有两个区间,则l1和l2至多有两个交集.固定l1的一个区间,若l2有k个区间,则l1与l2至多有k个交集.
如果l1有两个区间,则这两个区间与l2的某个区间至多有两个交集.通俗点来说,就是增加一个区间,至多增加一个交集.如果l1有k1个交集,则它与l2的一个区间至多有k1个交集,与l2的两个区间至多有k1+1个交集,与l2的三个区间至多有k1+2个交集,…与l2的k2个区间至多有k1+k2-1个交集.
可以看出交集的个数跟l1和l2的长度有关,这让我们思考是否有O(k1+k2)时间复杂度的算法得到l1和l2的所有的交集.
考虑l1和l2的两个元素l1[i]和l2[j].为了节省时间,更为了看起来简单,我们用[a,b]来表示l1[i],用[c,d]来表示l2[j].也就是:l1[i].mins = a, l1[i].maxs = b;l2[j].mins = c, l2[j].maxs = d.
根据上图1,在第一种时刻下,l1的某个元素l1[i]和l2的某个元素l2[j]他们的关系是:a < c和b < c.此时交集为空.
在第二种时刻下,a < c,c <= b <= d.这时候交集为[c, b].
第三种时刻有两种情况.当b-a<d-c时,这个时候a>=c,c<b<=d,这个时候的交集为[a, b];当b-a >= d-c时,这个时候a<c,b>d,这个时候的交集为[c, d];
第四种时刻下,c < a < d < b,这个时候的交集为[a, d];
第五种时刻下,d < a,交集为空.
根据以上情况,设计一个while循环并设置对应的if条件判断:
def getIntersection(l1, l2): #求交集方法
result = [] #用来存储l1和l2的所有交集
i = 0
j = 0
while i < len(l1) and j < len(l2