法算day14

[up主专用,视频内嵌代码贴在这]

合并区间

问题描述

以数组 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

问题分析

  1. 什么是区间重叠?
    两个区间 [a, b] 和 [c, d] 重叠的条件是:
  • 完全重叠:一个区间完全包含另一个区间,如 [1,4] 和 [2,3]
  • 部分重叠:一个区间的结束位置等于或大于另一个区间的起始位置,如 [1,3] 和 [2,6]
  • 边界重叠:一个区间的结束位置等于另一个区间的起始位置,如 [1,4] 和 [4,5]
  • 数学表达:如果 a <= d 且 c <= b,则两个区间重叠。
  1. 合并规则
    当两个区间重叠时,合并后的区间为:
  • 起始位置:取两个区间起始位置的较小值
  • 结束位置:取两个区间结束位置的较大值
  • 数学表达:合并 [a, b] 和 [c, d] 得到 [min(a, c), max(b, d)]

示例执行过程

以 intervals = [[1,3],[2,6],[8,10],[15,18]] 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
步骤1: 排序
输入: [[1,3],[2,6],[8,10],[15,18]]
已经是按起始位置排序的

步骤2: 初始化
merged = [[1,3]]

步骤3: 处理 [2,6]
当前区间: [2,6]
最后一个合并区间: [1,3]
判断: 2 <= 3 (重叠)
合并: 更新结束位置为 max(3,6)=6
merged = [[1,6]]

步骤4: 处理 [8,10]
当前区间: [8,10]
最后一个合并区间: [1,6]
判断: 8 <= 6? 否 (不重叠)
添加到合并列表
merged = [[1,6], [8,10]]

步骤5: 处理 [15,18]
当前区间: [15,18]
最后一个合并区间: [8,10]
判断: 15 <= 10? 否 (不重叠)
添加到合并列表
merged = [[1,6], [8,10], [15,18]]

最终结果: [[1,6],[8,10],[15,18]]

可视化示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
原始区间:
1-------3
2--------6
8-----10
15----18

排序后(已排序):
1-------3
2--------6
8-----10
15----18

合并:
[1,3] + [2,6] → [1,6]
[1,6] 与 [8,10] 不重叠 → 保留 [8,10]
[8,10] 与 [15,18] 不重叠 → 保留 [15,18]

结果:
1--------------6
8-----10
15----18

特殊情况分析

  • 情况1:完全包含
    1
    2
    3
    4
    5
    6
    intervals = [[1,4],[2,3]]
    合并过程:
    merged = [[1,4]]
    当前区间 [2,3]: 2 <= 4 (重叠)
    更新结束位置为 max(4,3)=4
    merged = [[1,4]]
  • 情况2:边界相等
    1
    2
    3
    4
    5
    6
    intervals = [[1,4],[4,5]]
    合并过程:
    merged = [[1,4]]
    当前区间 [4,5]: 4 <= 4 (重叠,注意边界相等也算重叠)
    更新结束位置为 max(4,5)=5
    merged = [[1,5]]
  • 情况3:顺序打乱
    1
    2
    3
    4
    5
    6
    7
    intervals = [[4,7],[1,4]]
    排序后: [[1,4],[4,7]]
    合并过程:
    merged = [[1,4]]
    当前区间 [4,7]: 4 <= 4 (重叠)
    更新结束位置为 max(4,7)=7
    merged = [[1,7]]

python版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def merge(intervals):
"""
合并所有重叠的区间

参数:
intervals: 区间列表,每个区间为 [start, end]

返回:
List[List[int]]: 合并后的不重叠区间列表
"""
# 如果区间列表为空或只有一个区间,直接返回
if len(intervals) <= 1:
return intervals

# 第一步:按照每个区间的起始位置进行排序
# 这样可以确保相邻的区间在位置上是有序的,便于合并
intervals.sort(key=lambda x: x[0])

# 初始化结果列表,先放入第一个区间
merged = [intervals[0]]

# 从第二个区间开始遍历
for i in range(1, len(intervals)):
# 获取当前区间
current_interval = intervals[i]
# 获取已合并区间列表中的最后一个区间
last_merged_interval = merged[-1]

# 判断当前区间是否与最后一个合并区间重叠
# 如果当前区间的起始位置 <= 最后一个合并区间的结束位置,说明有重叠
if current_interval[0] <= last_merged_interval[1]:
# 合并两个区间:更新最后一个合并区间的结束位置
# 取两个区间结束位置的较大值作为新的结束位置
last_merged_interval[1] = max(last_merged_interval[1], current_interval[1])
else:
# 如果没有重叠,直接将当前区间添加到合并列表中
merged.append(current_interval)

return merged

# 测试代码
if __name__ == "__main__":
# 示例1
intervals1 = [[1,3],[2,6],[8,10],[15,18]]
print(f"输入: intervals = {intervals1}")
print(f"输出: {merge(intervals1)}") # 输出: [[1,6],[8,10],[15,18]]
print(f"解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]")

# 示例2
intervals2 = [[1,4],[4,5]]
print(f"\n输入: intervals = {intervals2}")
print(f"输出: {merge(intervals2)}") # 输出: [[1,5]]
print(f"解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间")

# 示例3
intervals3 = [[4,7],[1,4]]
print(f"\n输入: intervals = {intervals3}")
print(f"输出: {merge(intervals3)}") # 输出: [[1,7]]
print(f"解释: 区间 [1,4] 和 [4,7] 可被视为重叠区间")

# 额外测试用例
intervals4 = [[1,4],[0,4]]
print(f"\n输入: intervals = {intervals4}")
print(f"输出: {merge(intervals4)}") # 输出: [[0,4]]

intervals5 = [[1,4],[2,3]]
print(f"\n输入: intervals = {intervals5}")
print(f"输出: {merge(intervals5)}") # 输出: [[1,4]]

✅ 说明:

  • 时间复杂度:O(n log n)
    排序操作:O(n log n)
    遍历合并:O(n)
  • 空间复杂度:O(n)
    存储合并后的区间列表

Java版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class MergeIntervals {

public int[][] merge(int[][] intervals) {
/**
* 合并所有重叠的区间
*
* @param intervals 区间数组,每个区间表示为 [start, end]
* @return 合并后的不重叠区间数组
*/

// 如果区间数组为空或只有一个区间,直接返回
if (intervals == null || intervals.length <= 1) {
return intervals;
}

// 1. 按照区间的起始位置进行排序
// 使用Comparator比较器,按照每个区间的第一个元素(起始位置)升序排序
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] a, int[] b) {
// 比较两个区间的起始位置
return Integer.compare(a[0], b[0]);
}
});

// 2. 使用List存储合并后的区间,方便动态添加
List<int[]> result = new ArrayList<>();

// 3. 将第一个区间添加到结果列表中作为起始
result.add(intervals[0]);

// 4. 遍历排序后的区间数组
for (int i = 1; i < intervals.length; i++) {
// 获取当前区间
int[] currentInterval = intervals[i];

// 获取结果列表中最后一个区间(即最近添加的区间)
int[] lastInterval = result.get(result.size() - 1);

// 5. 检查当前区间是否与最后一个区间重叠
// 如果当前区间的起始位置 <= 最后一个区间的结束位置,说明有重叠
if (currentInterval[0] <= lastInterval[1]) {
// 合并区间:更新最后一个区间的结束位置为两者结束位置的较大值
lastInterval[1] = Math.max(lastInterval[1], currentInterval[1]);
} else {
// 如果没有重叠,直接将当前区间添加到结果列表中
result.add(currentInterval);
}
}

// 6. 将List转换为二维数组返回
return result.toArray(new int[result.size()][]);
}

// 辅助方法:打印二维数组
public void printArray(int[][] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
System.out.print("[" + arr[i][0] + "," + arr[i][1] + "]");
if (i < arr.length - 1) {
System.out.print(",");
}
}
System.out.println("]");
}

// 测试代码
public static void main(String[] args) {
MergeIntervals solution = new MergeIntervals();

// 示例1
int[][] intervals1 = {{1,3}, {2,6}, {8,10}, {15,18}};
System.out.println("输入: intervals = [[1,3],[2,6],[8,10],[15,18]]");
System.out.print("输出: ");
solution.printArray(solution.merge(intervals1)); // 输出: [[1,6],[8,10],[15,18]]
System.out.println("解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].");

// 示例2
int[][] intervals2 = {{1,4}, {4,5}};
System.out.println("\n输入: intervals = [[1,4],[4,5]]");
System.out.print("输出: ");
solution.printArray(solution.merge(intervals2)); // 输出: [[1,5]]
System.out.println("解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间.");

// 示例3
int[][] intervals3 = {{4,7}, {1,4}};
System.out.println("\n输入: intervals = [[4,7],[1,4]]");
System.out.print("输出: ");
solution.printArray(solution.merge(intervals3)); // 输出: [[1,7]]
System.out.println("解释: 区间 [1,4] 和 [4,7] 可被视为重叠区间.");

// 额外测试用例
int[][] intervals4 = {{1,3}, {4,6}, {8,10}, {15,18}};
System.out.println("\n输入: intervals = [[1,3],[4,6],[8,10],[15,18]]");
System.out.print("输出: ");
solution.printArray(solution.merge(intervals4)); // 输出: [[1,3],[4,6],[8,10],[15,18]]
System.out.println("解释: 没有重叠区间,所有区间保持不变.");

int[][] intervals5 = {{1,10}, {2,5}, {6,8}, {9,12}};
System.out.println("\n输入: intervals = [[1,10],[2,5],[6,8],[9,12]]");
System.out.print("输出: ");
solution.printArray(solution.merge(intervals5)); // 输出: [[1,12]]
System.out.println("解释: 所有区间都有重叠,合并为一个大区间.");
}
}