每日一题
2025.8.20
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] 可被视为重叠区间。
提示:
1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104
总体思路
按起点升序(若起点相同则按终点升序)对区间排序。
这样做后,所有可能与某区间重叠的区间都会紧跟在它后面出现,便于线性扫描合并。
用一个变量 current 表示正在合并中的区间(初始化为排序后第一个)。
从左到右依次遍历后续区间 iv:
- 若 iv.start <= current.end,说明重叠/相邻(如 [1,4] 与 [4,5] 也要合并),更新:
current.end = max(current.end, iv.end) - 否则,说明没有重叠:把 current 推入结果数组,然后把 iv 设为新的 current。
扫描结束后,把最后一个 current 推入结果,完成合并。
时间复杂度:排序 O(n log n),扫描 O(n),总 O(n log n)
空间复杂度:结果需要 O(k)(k 为合并后区间数);除结果外只用到常数级额外空间。
代码
golang
go
// merge 合并所有重叠(或相邻)的区间。
// 输入输出均为 [][]int,其中每个区间为 [start, end]。
func merge(intervals [][]int) [][]int {
// 1) 特判:空输入或只有一个区间,直接返回
if len(intervals) <= 1 {
return intervals
}
// 2) 按起点升序排序;若起点相等,按终点升序
// sort.Slice 使用匿名比较函数,i、j 是索引,返回 true 表示 i 应排在 j 前面
sort.Slice(intervals, func(i, j int) bool {
if intervals[i][0] == intervals[j][0] {
return intervals[i][1] < intervals[j][1]
// 如果起点一样,就比较终点
}
// 否则,谁的起点小谁排前
return intervals[i][0] < intervals[j][0]
})
// 3) 准备结果切片。预分配容量可小优化:最多就是 n 个
res := make([][]int, 0, len(intervals))
// 4) current 表示当前正在合并的区间(直接引用排序后的第一个)
current := intervals[0] //(current[0],current[1])当前正在合并的区间
// 5) 线性扫描其余区间
for i := 1; i < len(intervals); i++ {
iv := intervals[i]
// 判断是否重叠/相邻:只要下一个区间的起点 <= 当前区间的终点
if iv[0] <= current[1] {
// 可以合并:更新 current 的右端点为更大的那个
if iv[1] > current[1] {
current[1] = iv[1]
}
// 注意:不立即放入 res,继续尝试与后面的区间合并
} else {
// 不重叠:把当前合并结果推入 res,并开始新的 current
//current := intervals[0] // 当前正在合并的区间
//iv := intervals[i] // 循环里取出来的一个新区间
res = append(res, current)
current = iv
}
}
// 6) 别忘了把最后一个合并完成的区间放进去
res = append(res, current)
return res
}
go
//无注释代码纯享
func merge(intervals [][]int) [][]int {
if len(intervals) <= 1 {
return intervals
}
sort.Slice(intervals,func(i, j int) bool {
if intervals[i][0] == intervals[j][0] {
return intervals[i][1] < intervals[j][1]
}
return intervals[i][0] < intervals[j][0]
})
res := make([][]int, 0, len(intervals))
current := intervals[0]
for i:=0; i<len(intervals); i++ {
iv := intervals[i]
if iv[0] <= current[1] {
if iv[1] > current[1] {
current[1] = iv[1]
}
}else{
res = append(res,current)
current = iv
}
}
res = append(res, current)
return res
}
golang知识
- sort.Slice 是什么?
Go 标准库里有个 sort 包,可以帮我们对切片(slice)进行排序。
它的常用函数有:
sort.Ints([]int) ------ 排序一维整型切片
sort.Strings([]string) ------ 排序字符串切片
sort.Slice(slice, lessFunc) ------ 最通用的,用来自定义规则排序任意切片。
- sort.Slice 的用法
它的函数签名大概是:
go
func Slice(slice interface{}, less func(i, j int) bool)
第一个参数:你要排序的切片(任何类型的都可以)
第二个参数:一个比较函数,形式是 func(i, j int) bool
i、j 是切片里的索引
返回值 true 表示:切片的第 i 个元素应该排在第 j 个元素前面
make([][]int, 0, len(intervals))
的三个参数
make(类型, 长度, 容量)
类型:[][]int
表示"切片的元素是 []int(区间),所以它是区间的列表"。
举例:[[1,3],[2,6]] 就是 [][]int。
长度:0
说明一开始里面没有任何元素(空切片)。
容量:len(intervals)
这里表示"预留容量 = 区间总数"。
因为最多结果里不会超过输入的数量(比如 4 个区间,合并后最多还是 4 个,不会更多)。
这样预先分配好空间,可以避免 append 时底层数组频繁扩容,稍微提高效率。