Leetcode刷题——8.重叠区间

8.重叠区间

重叠区间模式用于合并或处理数组中的重叠区间

在按开始时间排序的区间数组中,如果两个区间[a,b]和[c,d]重叠,则b>=c(即,第一个区间的结束时间大于或等于第二个区间的开始时间)。

核心:先排序,再遍历合并,通用解题步骤:

步骤 1:按区间起始点排序

先将所有区间按左端点(start)升序排序,这是处理重叠问题的前提。

排序后,我们只需要和已合并列表的最后一个区间比较,就能判断是否重叠。
步骤 2:初始化结果列表

创建一个空列表 merged,用来存放最终合并后的区间。

若输入区间列表为空,直接返回空。
步骤 3:遍历并合并区间

遍历排序后的每个区间 curr:

若 merged 为空,直接将 curr 加入 merged。

取 merged 中最后一个区间 last:
重叠判断 :curr.start <= last.end(因为已按 start 排序,只要当前区间的 start ≤ 上一个区间的 end,就一定重叠)。
重叠时合并 :更新 last.end 为 max(last.end, curr.end)(保留最大的结束值,覆盖重叠部分)。
不重叠时添加:将 curr 直接加入 merged。

场景 判断条件 操作
两个区间重叠 curr.start ≤ last.end 更新 last.end = max(last.end, curr.end)
两个区间不重叠 curr.start > last.end curr 追加到结果列表

合并区间(LeetCode #56)

题目描述

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]

输出:[[1,6],[8,10],[15,18]]

解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]

输出:[[1,5]]

解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

示例 3:

输入:intervals = [[4,7],[1,4]]

输出:[[1,7]]

解释:区间 [1,4] 和 [4,7] 可被视为重叠区间。

提示:

1 <= intervals.length <= 104

intervals[i].length == 2

0 <= starti <= endi <= 104

题目求解

python 复制代码
class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key=lambda x: x[0])
        merged = []
        for cur in intervals:
            if not merged:
                merged.append(cur)
            else:
                item = merged[-1]
                if item[0] <= cur[1] and cur[0] <= item[1]:
                    merged.pop(-1)
                    merged.append([min(item[0], cur[0]), max(item[1], cur[1])])
                else:
                    merged.append(cur)
        return merged

稍微优化下代码,把if...else...语句缩一下,当merged为空 或者 cur区间 和 item 区间 不重合,直接append;否则,合并,并且区间开始已经排好序了,所以肯定是最小值,不需要[min(item[0], cur[0])判断。代码如下:

python 复制代码
class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key=lambda x:x[0])
        merged = []
        for num in intervals:
            # 为空,或者 当前遍历的区间 和 merged最后一个区间 不重叠 
            if not merged or merged[-1][1] < num[0]:
                merged.append(num)
            else: 
                # 有重叠
                merged[-1][1] = max(merged[-1][1], num[1])
        return merged

插入区间(LeetCode#57)

题目描述

给你一个 无重叠的 ,按照区间起始端点排序的区间列表 intervals,其中 intervals[i] = [starti, endi] 表示第 i 个区间的开始和结束,并且 intervals 按照 starti 升序排列。同样给定一个区间 newInterval = [start, end] 表示另一个区间的开始和结束。

在 intervals 中插入区间 newInterval,使得 intervals 依然按照 starti 升序排列,且区间之间不重叠(如果有必要的话,可以合并区间)。

返回插入之后的 intervals。

注意 你不需要原地修改 intervals。你可以创建一个新数组然后返回它。

示例 1:

输入:intervals = [[1,3],[6,9]], newInterval = [2,5]

输出:[[1,5],[6,9]]

示例 2:

输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]

输出:[[1,2],[3,10],[12,16]]

解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。

提示:

0 <= intervals.length <= 104

intervals[i].length == 2

0 <= starti <= endi <= 105

intervals 根据 starti 按 升序 排列

newInterval.length == 2

0 <= start <= end <= 105

题目求解

把新的区间 append 到 区间列表中,其实就转为第一个题了

python 复制代码
class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        merged = intervals
        merged.append(newInterval)
        merged.sort(key=lambda x: x[0])
        ans = []
        for num in merged:
            if not ans or ans[-1][1] < num[0]:
                ans.append(num)
            else:
                ans[-1][1] = max(ans[-1][1], num[1])
        return ans

模拟------直接合并,但是需要考虑新的区间 的位置,思路:

  • 阶段 1:遍历所有「右端点 < 新区间左端点」的区间 → 直接加入结果(这些区间和新区间完全不重叠,且在新区间左侧)。
  • 阶段 2:遍历所有「左端点 ≤ 新区间右端点」的区间 → 合并这些区间和新区间(更新新区间的左右端点为合并后的极值)。
  • 阶段 3:将合并后的新区间加入结果,再遍历剩余所有区间 → 直接加入结果(这些区间在新区间右侧,完全不重叠)。
python 复制代码
# class Solution:
#     def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
#         merged = intervals
#         merged.append(newInterval)
#         merged.sort(key=lambda x: x[0])
#         ans = []
#         for num in merged:
#             if not ans or ans[-1][1] < num[0]:
#                 ans.append(num)
#             else:
#                 ans[-1][1] = max(ans[-1][1], num[1])
#         return ans

class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        i = 0
        n = len(intervals)
        ans = []
        # 右端点 < 新区间左端点 ->直接加入结果
        while i < n and intervals[i][1] < newInterval[0]:
            ans.append(intervals[i])
            i += 1
        # 看是否需要合并
        while i < n and intervals[i][0] <= newInterval[1]:
            newInterval[0] = min(intervals[i][0], newInterval[0])
            newInterval[1] = max(intervals[i][1], newInterval[1])
            i += 1
        ans.append(newInterval)
        # 添加剩余元素
        while i < n:
            ans.append(intervals[i])
            i += 1
        
        return ans

纯模拟,但很多小问题,不过还是很考虑细节的。

不重叠区间(LeetCode#435)

题目描述

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。

注意 只在一点上接触的区间是 不重叠的。例如 [1, 2] 和 [2, 3] 是不重叠的。

示例 1:

输入: intervals = [[1,2],[2,3],[3,4],[1,3]]

输出: 1

解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

输入: intervals = [ [1,2], [1,2], [1,2] ]

输出: 2

解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: intervals = [ [1,2], [2,3] ]

输出: 0

解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

提示:

1 <= intervals.length <= 105

intervals[i].length == 2

-5 * 104 <= starti < endi <= 5 * 104

题目求解

贪心策略:选结束早的,能给后面留出更多的空间。
生活类比(最易理解)

假设你是一个时间管理大师,要在一天内安排尽可能多的「不重叠会议」:

  • 会议 A:9:00-9:30(右端点 9:30)
  • 会议 B:9:15-10:00(右端点 10:00)
  • 会议 C:9:30-10:00(右端点 10:00)

你会优先选哪个?→ 选 A(9:00-9:30)!因为 A 结束得最早,选完 A 后还能选 C(9:30-10:00),总共能安排 2 个会议;→ 但如果选 B(9:15-10:00),就只能安排 1 个会议。

结论:选结束早(右端点小)的区间,能给后面留出更多「空闲时间」,从而容纳更多不重叠的区间。

优先选右端点最小的区间,能得到最多的不重叠区间数。(反证法 证得)

python 复制代码
class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if not intervals:
            return 0
        intervals.sort(key=lambda x: x[1])
        ans = 1 # 至少能保留的区间数
        n = len(intervals)
        last_end = intervals[0][1]
        for i in range(1, n):
            if intervals[i][0] >= last_end:
                ans += 1
                last_end = intervals[i][1]
        return n - ans

Python 中的排序 相关练习

python 复制代码
# list.sort()
# 基础排序
# 列表原地排序
nums = [3, 1, 4, 2, 7]
nums.sort()
print(nums)

# 降序
nums.sort(reverse=True)
print(nums)

# 返回新列表排序
nums_sorted = [6, 4, 2, 7, 1, 9]
sorted_nums = sorted(nums_sorted)
print(sorted_nums)
# 降序
sorted_nums_desc = sorted(nums_sorted, reverse=True)
print(sorted_nums_desc)

# 进阶排序
# 按元素某个属性
intervals = [[2, 3], [1, 4], [5, 6]]
intervals.sort(key=lambda x: x[0])
print(intervals)

intervals.sort(key=lambda x: x[1])
print(intervals)

# 排序 字典/对象
d = {"a": 3, "b": 1, "c": 2}
sorted_d = sorted(d.items(), key=lambda x: x[1])
print(sorted_d)

# 自定义 对象的属性排序
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

student = [Student('A', 88), Student('B', 71), Student('C', 98)]

# 按照分数升序
student.sort(key=lambda s: s.score)
print([s.name for s in student])


# 按照字符串长度排序
words = ['apple', 'banana', 'cat', 'dog']
words.sort(key=lambda w: len(w))
print(words)

# 多条件 排序
# 先按分数降序,分数相同按名字升序
students = [
    Student('Alice', 90),
    Student('Bob', 85),
    Student('Charlie', 90),
    Student('David', 85)
]
# key=(分数(负号实现降序),名字)
students.sort(key=lambda s: (-s.score, s.name))
print([(s.name, s.score) for s in students])

# 排序不可变对象
s = 'python'
sorted_s = sorted(s)
print(sorted_s) # ['h', 'n', 'o', 'p', 't', 'y']

# 排序元组
tup = (3, 1, 2, 7, 4, 5)
sorted_t = sorted(tup)
print(sorted_t) # 返回的是排序后的列表

# python中的 排序是稳定排序
students = [
    Student('Alice', 90),
    Student('Bob', 85),
    Student('Charlie', 90),
]
students.sort(key=lambda s: s.score)
print([s.name for s in students])
相关推荐
2401_857865231 小时前
C++模块接口设计
开发语言·c++·算法
add45a2 小时前
嵌入式C++低功耗设计
开发语言·c++·算法
DeepModel2 小时前
【概率分布】指数分布(Exponential Distribution)原理、推导与实战
python·算法·概率论
_饭团2 小时前
指针核心知识:5篇系统梳理3
c语言·数据结构·算法·leetcode·面试·学习方法·改行学it
2401_874732532 小时前
C++中的状态模式
开发语言·c++·算法
BB学长2 小时前
LBM vs FVM:谁才是 CFD 的未来?
人工智能·算法·机器学习
闻缺陷则喜何志丹2 小时前
【枚举】P6786「SWTR-6」GCDs & LCMs|普及+
c++·算法·洛谷
m0_716667072 小时前
实时数据压缩库
开发语言·c++·算法
dapeng28702 小时前
多协议网络库设计
开发语言·c++·算法