区间列表的交集

标签: 数组 双指针

难度: Medium

给定两个由一些 闭区间 组成的列表,firstListsecondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序

返回这 两个区间列表的交集

形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b

两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3][2, 4] 的交集为 [2, 3]

 

示例 1:

输入:firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]

示例 2:

输入:firstList = [[1,3],[5,9]], secondList = []
输出:[]

示例 3:

输入:firstList = [], secondList = [[4,8],[10,12]]
输出:[]

示例 4:

输入:firstList = [[1,7]], secondList = [[3,10]]
输出:[[3,7]]

 

提示:

  • 0 <= firstList.length, secondList.length <= 1000
  • firstList.length + secondList.length >= 1
  • 0 <= starti < endi <= 109
  • endi < starti+1
  • 0 <= startj < endj <= 109
  • endj < startj+1

Submission

运行时间: 32 ms

内存: 17.0 MB

class Solution:
    def intervalIntersection(self, firstList: List[List[int]], secondList: List[List[int]]) -> List[List[int]]:
        i, j = 0, 0
        res = []
        print(f"{firstList}, {secondList}")
        while i < len(firstList) and j < len(secondList):
            a1, a2 = firstList[i][0], firstList[i][1]
            b1, b2 = secondList[j][0], secondList[j][1]
            if b2 >= a1 and a2 >= b1:
                # 区间相交
                res.append([max(a1, b1), min(a2, b2)])
            #
            if a2 < b2:
                i += 1
            else:
                j += 1
        #
        return res

Explain

该题解采用了双指针技术遍历两个输入的区间列表。两个指针分别指向firstList和secondList的当前区间。通过比较两个当前区间的起始和结束位置来判断它们是否相交,并计算交集。如果一个区间的结束位置小于另一个区间的结束位置,则移动该区间的指针以检查下一个区间,否则移动另一个区间的指针。这样可以保证每个区间只被访问一次,直到一个列表的区间被全部访问完毕。

时间复杂度: O(M+N)

空间复杂度: O(min(M, N))

class Solution:
    def intervalIntersection(self, firstList: List[List[int]], secondList: List[List[int]]) -> List[List[int]]:
        i, j = 0, 0
        res = []
        while i < len(firstList) and j < len(secondList):
            a1, a2 = firstList[i][0], firstList[i][1]
            b1, b2 = secondList[j][0], secondList[j][1]
            # 检查区间[firstList[i] 和 secondList[j]]是否相交
            if b2 >= a1 and a2 >= b1:
                # 如果相交,计算交集区间并添加到结果列表
                res.append([max(a1, b1), min(a2, b2)])
            # 移动结束时间较早的区间的指针
            if a2 < b2:
                i += 1
            else:
                j += 1
        return res

Explore

双指针技术在处理区间列表的交集问题时能够有效地提高算法的效率和减少不必要的比较。这种方法通过在两个列表上各自维护一个指针,只对当前的区间进行比较和处理,避免了对所有区间的全面比较。一旦确定两个区间是否相交,并处理完毕(例如记录交集),就可以根据区间的结束时间来决定移动哪一个指针。这样,每个区间最多被访问一次,从而减少了时间复杂度到线性级别,即O(n+m),其中n和m是两个列表的长度。

算法通过逐步移动结束时间较早的区间的指针来确保每个区间只被处理一次而不会被重复处理。在每步操作中,只有当前比较的两个区间中结束时间较早的那个区间的指针会向前移动,这样可以保证每个区间在被比较后即被排除在后续的比较之外。这种方式有效地避免了对已经处理过的区间的重复处理,并确保了所有区间都会被逐个检查。

当一个区间完全包含另一个区间时,双指针方法仍然能够有效地计算交集。在这种情况下,被完全包含的区间与包含它的区间的交集实际上就是被包含区间本身。算法通过计算两个区间的最大起始点和最小结束点来确定交集区间。如果一个区间完全包含另一个,那么较小的区间(被包含者)的起始点和结束点会决定交集的边界。在这之后,结束时间较早的区间的指针将向前移动,以检查可能的新的交集区间。

在双指针遍历中,即使两个列表的长度差异很大,该方法仍然是效率较高的。因为算法的时间复杂度主要依赖于较短的列表,因为一旦较短列表中的所有区间都被处理完,算法就会终止。这意味着算法的时间复杂度是O(min(n,m)),其中n和m分别是两个列表的长度。因此,即使其中一个列表特别长,只要另一个列表的长度较短,算法仍然能够快速地完成处理。