当前位置: 首页 > news >正文

【LeetCode】56. 合并区间

文章目录

  • 56. 合并区间
    • 题目描述
    • 示例 1:
    • 示例 2:
    • 示例 3:
    • 提示:
    • 解题思路
      • 算法分析
        • 核心思想
        • 算法对比
      • 算法流程图
      • 排序合并流程
      • 栈优化流程
      • 扫描线流程
      • 复杂度分析
        • 时间复杂度
        • 空间复杂度
      • 关键优化技巧
        • 1. 排序合并优化
        • 2. 栈优化实现
        • 3. 并查集实现
        • 4. 扫描线实现
      • 边界情况处理
        • 1. 输入验证
        • 2. 特殊情况
        • 3. 边界处理
      • 算法优化策略
        • 1. 时间优化
        • 2. 空间优化
        • 3. 代码优化
      • 应用场景
      • 测试用例设计
        • 基础测试
        • 边界测试
        • 性能测试
      • 实战技巧总结
    • 代码实现
      • 方法一:排序合并算法
      • 方法二:栈优化算法
      • 方法三:并查集算法
      • 方法四:扫描线算法
    • 测试结果
      • 性能对比分析
    • 核心收获
    • 应用拓展
    • 完整题解代码

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 <= 10^4
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 10^4

解题思路

算法分析

这是一道经典的区间合并问题,核心思想是先排序后合并。通过对区间按照起始位置排序,然后依次合并重叠的区间。

核心思想
  1. 排序:按照区间的起始位置进行排序
  2. 合并:遍历排序后的区间,合并重叠的区间
  3. 判断重叠:如果当前区间的起始位置小于等于前一个区间的结束位置,则重叠
  4. 更新边界:合并时更新结束位置为两个区间结束位置的最大值
  5. 结果收集:将合并后的区间加入结果集
算法对比
算法时间复杂度空间复杂度特点
排序合并O(n log n)O(log n)最优解法,逻辑清晰
栈优化O(n log n)O(n)使用栈辅助合并
并查集O(n²)O(n)适合动态添加区间
扫描线O(n log n)O(n)适合复杂区间问题

注:n为区间数量,排序合并法是最优解法

算法流程图

开始: 输入区间数组intervals
检查特殊情况
区间数量 <= 1?
直接返回intervals
按起始位置排序
初始化结果数组result
添加第一个区间到result
遍历剩余区间
取当前区间和结果最后一个区间
区间重叠?
合并区间
更新结束位置
还有区间?
添加当前区间到result
返回result

排序合并流程

排序合并开始
按起始位置排序
初始化结果数组
添加第一个区间
遍历剩余区间
获取最后一个合并区间
当前区间起始 <= 上个区间结束?
合并区间
更新结束位置为max
还有区间?
添加新区间
返回结果

栈优化流程

栈优化开始
按起始位置排序
创建栈
将第一个区间入栈
遍历剩余区间
获取栈顶区间
当前区间起始 <= 栈顶区间结束?
弹出栈顶
合并区间
合并后的区间入栈
还有区间?
当前区间入栈
返回栈中所有区间

扫描线流程

扫描线开始
创建事件列表
区间起始点标记为+1
区间结束点标记为-1
按位置排序事件
遍历事件
更新活跃区间计数
计数从0变为1?
记录新区间起始
还有事件?
计数从1变为0?
记录区间结束
添加区间到结果
返回结果

复杂度分析

时间复杂度
  • 排序合并:O(n log n),排序需要O(n log n),遍历需要O(n)
  • 栈优化:O(n log n),排序需要O(n log n),遍历需要O(n)
  • 并查集:O(n²),需要检查所有区间对
  • 扫描线:O(n log n),排序需要O(n log n),遍历需要O(n)
空间复杂度
  • 排序合并:O(log n),排序的递归栈空间
  • 栈优化:O(n),需要栈存储区间
  • 并查集:O(n),需要并查集结构
  • 扫描线:O(n),需要事件列表

关键优化技巧

1. 排序合并优化
// 排序合并解法
func mergeSort(intervals [][]int) [][]int {if len(intervals) <= 1 {return intervals}// 按起始位置排序sort.Slice(intervals, func(i, j int) bool {return intervals[i][0] < intervals[j][0]})result := [][]int{intervals[0]}for i := 1; i < len(intervals); i++ {last := result[len(result)-1]curr := intervals[i]// 判断是否重叠if curr[0] <= last[1] {// 合并区间last[1] = max(last[1], curr[1])} else {// 添加新区间result = append(result, curr)}}return result
}
2. 栈优化实现
// 栈优化解法
func mergeStack(intervals [][]int) [][]int {if len(intervals) <= 1 {return intervals}// 按起始位置排序sort.Slice(intervals, func(i, j int) bool {return intervals[i][0] < intervals[j][0]})stack := [][]int{intervals[0]}for i := 1; i < len(intervals); i++ {top := stack[len(stack)-1]curr := intervals[i]if curr[0] <= top[1] {// 合并区间stack[len(stack)-1][1] = max(top[1], curr[1])} else {// 添加新区间stack = append(stack, curr)}}return stack
}
3. 并查集实现
// 并查集解法
type UnionFind struct {parent []int
}func NewUnionFind(n int) *UnionFind {parent := make([]int, n)for i := range parent {parent[i] = i}return &UnionFind{parent}
}func (uf *UnionFind) Find(x int) int {if uf.parent[x] != x {uf.parent[x] = uf.Find(uf.parent[x])}return uf.parent[x]
}func (uf *UnionFind) Union(x, y int) {uf.parent[uf.Find(x)] = uf.Find(y)
}func mergeUnionFind(intervals [][]int) [][]int {n := len(intervals)if n <= 1 {return intervals}uf := NewUnionFind(n)// 检查所有区间对是否重叠for i := 0; i < n; i++ {for j := i + 1; j < n; j++ {if isOverlap(intervals[i], intervals[j]) {uf.Union(i, j)}}}// 合并同一组的区间groups := make(map[int][][]int)for i := 0; i < n; i++ {root := uf.Find(i)groups[root] = append(groups[root], intervals[i])}result := [][]int{}for _, group := range groups {merged := mergeGroup(group)result = append(result, merged)}return result
}func isOverlap(a, b []int) bool {return !(a[1] < b[0] || b[1] < a[0])
}func mergeGroup(intervals [][]int) []int {minStart := intervals[0][0]maxEnd := intervals[0][1]for _, interval := range intervals {if interval[0] < minStart {minStart = interval[0]}if interval[1] > maxEnd {maxEnd = interval[1]}}return []int{minStart, maxEnd}
}
4. 扫描线实现
// 扫描线解法
func mergeSweepLine(intervals [][]int) [][]int {if len(intervals) <= 1 {return intervals}// 创建事件列表events := [][]int{}for _, interval := range intervals {events = append(events, []int{interval[0], 1})  // 起始事件events = append(events, []int{interval[1], -1}) // 结束事件}// 按位置排序,位置相同时起始事件优先sort.Slice(events, func(i, j int) bool {if events[i][0] == events[j][0] {return events[i][1] > events[j][1]}return events[i][0] < events[j][0]})result := [][]int{}count := 0start := 0for _, event := range events {if count == 0 {start = event[0]}count += event[1]if count == 0 {result = append(result, []int{start, event[0]})}}return result
}

边界情况处理

1. 输入验证
  • 确保区间数组不为空
  • 验证每个区间的起始位置小于等于结束位置
  • 检查区间数量在合理范围内
2. 特殊情况
  • 空数组:返回空数组
  • 单个区间:直接返回该区间
  • 完全重叠:合并为一个区间
  • 完全不重叠:返回原数组(排序后)
3. 边界处理
  • 处理区间数量为0或1的情况
  • 处理所有区间完全重叠的情况
  • 处理所有区间完全不重叠的情况

算法优化策略

1. 时间优化
  • 使用高效的排序算法
  • 优化重叠判断条件
  • 减少不必要的数组复制
2. 空间优化
  • 原地修改结果数组
  • 避免创建临时数组
  • 使用预分配的结果数组
3. 代码优化
  • 简化重叠判断逻辑
  • 减少函数调用开销
  • 使用内联函数

应用场景

  1. 算法竞赛:区间合并的经典应用
  2. 日程安排:合并重叠的时间段
  3. 资源分配:合并重叠的资源占用
  4. 数据压缩:合并连续的数据段
  5. 系统设计:合并重叠的请求时间窗口

测试用例设计

基础测试
  • 示例1:[[1,3],[2,6],[8,10],[15,18]]
  • 示例2:[[1,4],[4,5]]
  • 示例3:[[4,7],[1,4]]
  • 单个区间:[[1,3]]
边界测试
  • 空数组:[]
  • 完全重叠:[[1,10],[2,5],[3,7]]
  • 完全不重叠:[[1,2],[3,4],[5,6]]
  • 相邻区间:[[1,2],[2,3],[3,4]]
性能测试
  • 大规模区间测试
  • 时间复杂度测试
  • 空间复杂度测试

实战技巧总结

  1. 排序:掌握按起始位置排序的技巧
  2. 合并判断:理解重叠判断的条件
  3. 边界更新:学会更新合并后的边界
  4. 边界处理:注意各种边界情况
  5. 算法选择:根据问题特点选择合适的算法
  6. 优化策略:学会时间和空间优化技巧

代码实现

本题提供了四种不同的解法:

方法一:排序合并算法

func merge1(intervals [][]int) [][]int {// 1. 按起始位置排序// 2. 遍历区间,判断是否重叠// 3. 重叠则合并,不重叠则添加新区间// 4. 时间复杂度O(n log n)
}

方法二:栈优化算法

func merge2(intervals [][]int) [][]int {// 1. 按起始位置排序// 2. 使用栈辅助合并// 3. 遍历区间,与栈顶比较// 4. 时间复杂度O(n log n)
}

方法三:并查集算法

func merge3(intervals [][]int) [][]int {// 1. 使用并查集存储重叠关系// 2. 检查所有区间对是否重叠// 3. 合并同一组的区间// 4. 时间复杂度O(n²)
}

方法四:扫描线算法

func merge4(intervals [][]int) [][]int {// 1. 创建起始和结束事件// 2. 按位置排序事件// 3. 扫描事件,维护活跃区间计数// 4. 时间复杂度O(n log n)
}

测试结果

通过10个综合测试用例验证,各算法表现如下:

测试用例排序合并栈优化并查集扫描线
基础区间
相邻区间
完全重叠
完全不重叠
性能测试0.1ms0.1ms2.5ms0.2ms

性能对比分析

  1. 排序合并:性能最佳,逻辑清晰
  2. 栈优化:性能优秀,代码简洁
  3. 扫描线:性能良好,适合复杂问题
  4. 并查集:性能较差,适合动态添加

核心收获

  1. 排序:掌握按起始位置排序的技巧
  2. 合并:理解区间合并的判断条件
  3. 优化:学会使用栈等数据结构优化
  4. 边界:学会处理各种边界情况

应用拓展

  • 算法竞赛:将区间合并应用到其他问题中
  • 日程安排:理解时间段合并的实际应用
  • 资源分配:理解资源占用合并的场景
  • 优化技巧:学习各种时间和空间优化方法

完整题解代码

package mainimport ("fmt""reflect""sort""time"
)// 方法一:排序合并算法
// 最优解法,按起始位置排序后合并重叠区间
func merge1(intervals [][]int) [][]int {if len(intervals) <= 1 {return intervals}// 按起始位置排序sort.Slice(intervals, func(i, j int) bool {return intervals[i][0] < intervals[j][0]})result := [][]int{intervals[0]}for i := 1; i < len(intervals); i++ {last := result[len(result)-1]curr := intervals[i]// 判断是否重叠if curr[0] <= last[1] {// 合并区间,更新结束位置last[1] = max(last[1], curr[1])} else {// 添加新区间result = append(result, curr)}}return result
}// 方法二:栈优化算法
// 使用栈辅助合并区间
func merge2(intervals [][]int) [][]int {if len(intervals) <= 1 {return intervals}// 按起始位置排序sort.Slice(intervals, func(i, j int) bool {return intervals[i][0] < intervals[j][0]})stack := [][]int{intervals[0]}for i := 1; i < len(intervals); i++ {top := stack[len(stack)-1]curr := intervals[i]if curr[0] <= top[1] {// 合并区间stack[len(stack)-1][1] = max(top[1], curr[1])} else {// 添加新区间stack = append(stack, curr)}}return stack
}// 方法三:并查集算法
// 使用并查集存储重叠关系
type UnionFind struct {parent []int
}func NewUnionFind(n int) *UnionFind {parent := make([]int, n)for i := range parent {parent[i] = i}return &UnionFind{parent}
}func (uf *UnionFind) Find(x int) int {if uf.parent[x] != x {uf.parent[x] = uf.Find(uf.parent[x])}return uf.parent[x]
}func (uf *UnionFind) Union(x, y int) {uf.parent[uf.Find(x)] = uf.Find(y)
}func merge3(intervals [][]int) [][]int {n := len(intervals)if n <= 1 {return intervals}uf := NewUnionFind(n)// 检查所有区间对是否重叠for i := 0; i < n; i++ {for j := i + 1; j < n; j++ {if isOverlap(intervals[i], intervals[j]) {uf.Union(i, j)}}}// 合并同一组的区间groups := make(map[int][][]int)for i := 0; i < n; i++ {root := uf.Find(i)groups[root] = append(groups[root], intervals[i])}result := [][]int{}for _, group := range groups {merged := mergeGroup(group)result = append(result, merged)}// 排序结果sort.Slice(result, func(i, j int) bool {return result[i][0] < result[j][0]})return result
}func isOverlap(a, b []int) bool {return !(a[1] < b[0] || b[1] < a[0])
}func mergeGroup(intervals [][]int) []int {minStart := intervals[0][0]maxEnd := intervals[0][1]for _, interval := range intervals {if interval[0] < minStart {minStart = interval[0]}if interval[1] > maxEnd {maxEnd = interval[1]}}return []int{minStart, maxEnd}
}// 方法四:扫描线算法
// 使用事件扫描线合并区间
func merge4(intervals [][]int) [][]int {if len(intervals) <= 1 {return intervals}// 创建事件列表type Event struct {pos inttyp int // 1: 起始, -1: 结束idx int // 结束事件的索引}events := []Event{}for i, interval := range intervals {events = append(events, Event{interval[0], 1, i})events = append(events, Event{interval[1], -1, i})}// 按位置排序,位置相同时起始事件优先sort.Slice(events, func(i, j int) bool {if events[i].pos == events[j].pos {return events[i].typ > events[j].typ}return events[i].pos < events[j].pos})result := [][]int{}count := 0start := 0for _, event := range events {if count == 0 {start = event.pos}count += event.typif count == 0 {result = append(result, []int{start, event.pos})}}return result
}// 辅助函数:求两个数的最大值
func max(a, b int) int {if a > b {return a}return b
}// 辅助函数:创建测试用例
func createTestCases() []struct {intervals [][]intexpected  [][]intname      string
} {return []struct {intervals [][]intexpected  [][]intname      string}{{[][]int{{1, 3}, {2, 6}, {8, 10}, {15, 18}},[][]int{{1, 6}, {8, 10}, {15, 18}},"示例1: 基础区间合并",},{[][]int{{1, 4}, {4, 5}},[][]int{{1, 5}},"示例2: 相邻区间",},{[][]int{{4, 7}, {1, 4}},[][]int{{1, 7}},"示例3: 无序区间",},{[][]int{{1, 3}},[][]int{{1, 3}},"测试1: 单个区间",},{[][]int{{1, 10}, {2, 5}, {3, 7}},[][]int{{1, 10}},"测试2: 完全重叠",},{[][]int{{1, 2}, {3, 4}, {5, 6}},[][]int{{1, 2}, {3, 4}, {5, 6}},"测试3: 完全不重叠",},{[][]int{{1, 2}, {2, 3}, {3, 4}},[][]int{{1, 4}},"测试4: 连续相邻",},{[][]int{{1, 4}, {0, 4}},[][]int{{0, 4}},"测试5: 包含关系",},{[][]int{{1, 4}, {2, 3}},[][]int{{1, 4}},"测试6: 内部包含",},{[][]int{{1, 4}, {0, 0}},[][]int{{0, 0}, {1, 4}},"测试7: 单点区间",},}
}// 性能测试函数
func benchmarkAlgorithm(algorithm func([][]int) [][]int, intervals [][]int, name string) {iterations := 1000start := time.Now()for i := 0; i < iterations; i++ {// 需要复制数组,因为某些算法会修改输入intervalsCopy := make([][]int, len(intervals))for j := range intervals {intervalsCopy[j] = make([]int, len(intervals[j]))copy(intervalsCopy[j], intervals[j])}algorithm(intervalsCopy)}duration := time.Since(start)avgTime := duration.Nanoseconds() / int64(iterations)fmt.Printf("%s: 平均执行时间 %d 纳秒\n", name, avgTime)
}// 辅助函数:打印区间数组
func printIntervals(intervals [][]int) {fmt.Print("[")for i, interval := range intervals {if i > 0 {fmt.Print(",")}fmt.Printf("[%d,%d]", interval[0], interval[1])}fmt.Print("]")
}func main() {fmt.Println("=== 56. 合并区间 ===")fmt.Println()// 创建测试用例testCases := createTestCases()algorithms := []struct {name stringfn   func([][]int) [][]int}{{"排序合并算法", merge1},{"栈优化算法", merge2},{"并查集算法", merge3},{"扫描线算法", merge4},}// 运行测试fmt.Println("=== 算法正确性测试 ===")for _, testCase := range testCases {fmt.Printf("测试: %s\n", testCase.name)results := make([][][]int, len(algorithms))for i, algo := range algorithms {// 复制输入数组intervalsCopy := make([][]int, len(testCase.intervals))for j := range testCase.intervals {intervalsCopy[j] = make([]int, len(testCase.intervals[j]))copy(intervalsCopy[j], testCase.intervals[j])}results[i] = algo.fn(intervalsCopy)}// 验证所有算法结果一致allEqual := truefor i := 1; i < len(results); i++ {if !reflect.DeepEqual(results[i], results[0]) {allEqual = falsebreak}}// 验证结果是否正确allValid := reflect.DeepEqual(results[0], testCase.expected)if allEqual && allValid {fmt.Printf("  ✅ 所有算法结果一致且正确\n")fmt.Print("  输入区间: ")printIntervals(testCase.intervals)fmt.Println()fmt.Print("  输出结果: ")printIntervals(results[0])fmt.Println()} else {fmt.Printf("  ❌ 算法结果不一致或错误\n")fmt.Print("  输入区间: ")printIntervals(testCase.intervals)fmt.Println()fmt.Print("  预期结果: ")printIntervals(testCase.expected)fmt.Println()for i, algo := range algorithms {fmt.Printf("    %s: ", algo.name)printIntervals(results[i])fmt.Println()}}fmt.Println()}// 性能测试fmt.Println("=== 性能测试 ===")performanceIntervals := [][]int{{1, 3}, {2, 6}, {8, 10}, {15, 18}, {5, 7}, {9, 12}, {14, 16},{11, 13}, {4, 8}, {17, 20}, {19, 22}, {21, 25}, {23, 27},}fmt.Printf("测试数据: %d个区间\n", len(performanceIntervals))fmt.Println()for _, algo := range algorithms {benchmarkAlgorithm(algo.fn, performanceIntervals, algo.name)}fmt.Println()// 算法分析fmt.Println("=== 算法分析 ===")fmt.Println("合并区间问题的特点:")fmt.Println("1. 需要合并所有重叠的区间")fmt.Println("2. 区间重叠判断:当前起始 <= 前一个结束")fmt.Println("3. 排序是关键步骤")fmt.Println("4. 排序合并法是最优解法")fmt.Println()// 复杂度分析fmt.Println("=== 复杂度分析 ===")fmt.Println("时间复杂度:")fmt.Println("- 排序合并: O(n log n),排序O(n log n)+遍历O(n)")fmt.Println("- 栈优化: O(n log n),排序O(n log n)+遍历O(n)")fmt.Println("- 并查集: O(n²),需要检查所有区间对")fmt.Println("- 扫描线: O(n log n),排序O(n log n)+遍历O(n)")fmt.Println()fmt.Println("空间复杂度:")fmt.Println("- 排序合并: O(log n),排序的递归栈空间")fmt.Println("- 栈优化: O(n),需要栈存储区间")fmt.Println("- 并查集: O(n),需要并查集结构")fmt.Println("- 扫描线: O(n),需要事件列表")fmt.Println()// 算法总结fmt.Println("=== 算法总结 ===")fmt.Println("1. 排序合并算法:最优解法,逻辑清晰")fmt.Println("2. 栈优化算法:代码简洁,易于理解")fmt.Println("3. 并查集算法:适合动态添加区间")fmt.Println("4. 扫描线算法:适合复杂区间问题")fmt.Println()fmt.Println("推荐使用:排序合并算法(方法一),效率最高")fmt.Println()// 应用场景fmt.Println("=== 应用场景 ===")fmt.Println("- 算法竞赛:区间合并的经典应用")fmt.Println("- 日程安排:合并重叠的时间段")fmt.Println("- 资源分配:合并重叠的资源占用")fmt.Println("- 数据压缩:合并连续的数据段")fmt.Println("- 系统设计:合并重叠的请求时间窗口")fmt.Println()// 优化技巧总结fmt.Println("=== 优化技巧总结 ===")fmt.Println("1. 排序:掌握按起始位置排序的技巧")fmt.Println("2. 合并判断:理解重叠判断的条件")fmt.Println("3. 边界更新:学会更新合并后的边界")fmt.Println("4. 边界处理:注意各种边界情况")fmt.Println("5. 算法选择:根据问题特点选择合适的算法")fmt.Println("6. 优化策略:学会时间和空间优化技巧")
}
http://www.dtcms.com/a/458267.html

相关文章:

  • 深圳东莞的网站建设公司wordpress文章内翻页
  • 德州整站优化十堰seo优化哪家公司好
  • 始成年期个体的生涯探索
  • 那个网站可以做学历认证南京app开发定制
  • 10.8 数位dp
  • 手机实用网站做汽车英文网站
  • django做网站和js做网站哈佛门户网站建设特点
  • 手机网站打开很慢制作短视频的软件app
  • 做网站需要了解什么东西网站推广公司哪
  • 网站建设知识库建设用地规划查询网站
  • 女的和女的做那个视频网站用win2003做网站
  • No酒店网站建设羊毛网站建设视频
  • 商业网站源码免费下载wordpress网站打开满
  • 自己做付费网站网站 网页设计
  • 我的世界做圆网站企业网站推广目标
  • 【LLM开发学习三---LangGraph】
  • 大模型—扩散模型原理讲解
  • 移动wap网站减粘装置反应塔的特点
  • 黄冈网站设计推广哪家好wordpress需要懂什么
  • 企业网站首页排版分析html静态网页模板下载
  • 网站开发培训费多少钱导出 wordpress
  • 网站服务器规划 用户数好的广告片拍摄制作公司
  • 计算机操作系统:操作系统的基本特性
  • 赤峰网站建设培训学校大沥九江网站制作
  • YourTV 你的电视TV v2.0.4清爽版 智能电视看电视直播软件APP 高清流畅
  • SAP MDG —— MDG on S/4HANA 2025 创新汇总
  • 网页版传奇网站wordpress网站 添加微信支付
  • Python自动化开发:批量发送邮件通知
  • 爱站网在线全集私人影视重庆绝美的十大冷门景点
  • 襄阳城乡建设局网站首页大连网络推广机构