Leetcode刷题——8.重叠区间

8.重叠区间

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

在按开始时间排序的区间数组中,如果两个区间a,bc,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 表示若干个区间的集合,其中单个区间为 intervalsi = starti, endi 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

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

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

解释:区间 1,32,6 重叠, 将它们合并为 1,6.

示例 2:

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

输出:\[1,5]

解释:区间 1,44,5 可被视为重叠区间。

示例 3:

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

输出:\[1,7]

解释:区间 1,44,7 可被视为重叠区间。

提示:

1 <= intervals.length <= 104

intervalsi.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, cur0)判断。代码如下:

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,其中 intervalsi = 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,83,5,6,7,8,10 重叠。

提示:

0 <= intervals.length <= 104

intervalsi.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 ,其中 intervalsi = starti, endi 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。

注意 只在一点上接触的区间是 不重叠的。例如 1, 22, 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

intervalsi.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])
相关推荐
仍然.18 小时前
算法题目---BFS解决最短路问题
算法·宽度优先
渡众机器人18 小时前
第八届全球校园人工智能算法精英大赛-算法应用赛-空地协同侦排挑战赛规则
人工智能·算法
wayz1118 小时前
Overlap:HWMA(Holt-Winter移动平均线)技术指标详解
算法·金融·数据分析·量化交易·特征工程
Shadow(⊙o⊙)19 小时前
专题四:前缀和
数据结构·算法
JAVA面经实录91719 小时前
高频算法面试题
java·计算机网络·算法·面试
qq_4523962319 小时前
第十一篇:《资源管理:Requests/Limits、ResourceQuota、LimitRange》
算法·贪心算法
Tisfy19 小时前
LeetCode 2095.删除链表的中间节点:两次遍历 / 一次遍历(快慢指针)
算法·leetcode·链表·题解·双指针
Irissgwe19 小时前
AVL树详解
数据结构·c++·算法·二叉树·c·二叉搜索树·avl
凌波粒19 小时前
LeetCode--131.分割回文串(回溯算法)
算法·leetcode·职场和发展
北域码匠20 小时前
奇偶归并排序:并行计算的排序利器
数据结构·算法·c#·排序算法