【LeetCode 热题100】23:合并 K 个升序链表(详细解析)(Go语言版)

简介: 本文详细解析了 LeetCode 热题 23——合并 K 个升序链表的两种解法:优先队列(最小堆)和分治合并。题目要求将多个已排序链表合并为一个升序链表。最小堆方法通过维护节点优先级快速选择最小值,;分治合并则采用归并思想两两合并链表。文章提供了 Go 语言实现代码,并对比分析两种方法的适用场景,帮助读者深入理解链表操作与算法设计。

🚀 LeetCode 热题 23:合并 K 个升序链表(详细解析)

📌 题目描述

LeetCode 23. Merge k Sorted Lists

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。


🎯 示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]

🎯 示例 2:

输入:lists = []
输出:[]

🎯 示例 3:

输入:lists = [[]]
输出:[]

💡 解题思路一:优先队列(最小堆)

使用最小堆(Priority Queue)维护 k 个链表的头节点,每次取堆顶最小值节点,接入结果链表。

✅ 步骤解析

  1. 初始化最小堆,将每个非空链表的头节点入堆。
  2. 不断从堆中弹出最小值节点,将其加入结果链表。
  3. 如果弹出的节点有 next 节点,则将 next 节点入堆。
  4. 最后返回结果链表头部。

💻 Go 实现(使用 heap 接口)

type ListNode struct {
   
    Val  int
    Next *ListNode
}

type NodeHeap []*ListNode

func (h NodeHeap) Len() int           {
    return len(h) }
func (h NodeHeap) Less(i, j int) bool {
    return h[i].Val < h[j].Val }
func (h NodeHeap) Swap(i, j int)      {
    h[i], h[j] = h[j], h[i] }

func (h *NodeHeap) Push(x interface{
   }) {
   
    *h = append(*h, x.(*ListNode))
}

func (h *NodeHeap) Pop() interface{
   } {
   
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

func mergeKLists(lists []*ListNode) *ListNode {
   
    h := &NodeHeap{
   }
    heap.Init(h)

    for _, node := range lists {
   
        if node != nil {
   
            heap.Push(h, node)
        }
    }

    dummy := &ListNode{
   }
    curr := dummy

    for h.Len() > 0 {
   
        node := heap.Pop(h).(*ListNode)
        curr.Next = node
        curr = curr.Next
        if node.Next != nil {
   
            heap.Push(h, node.Next)
        }
    }

    return dummy.Next
}

💡 解题思路二:分治合并(归并)

  • 将 k 个链表两两合并,逐步缩小范围。
  • 类似归并排序的思想。

✅ 步骤解析

  1. 每次将链表对半拆分,递归合并每一对链表。
  2. 使用合并两个有序链表的经典方法。

💻 Go 实现

func mergeKLists(lists []*ListNode) *ListNode {
   
    if len(lists) == 0 {
   
        return nil
    }
    return mergeRange(lists, 0, len(lists)-1)
}

func mergeRange(lists []*ListNode, l, r int) *ListNode {
   
    if l == r {
   
        return lists[l]
    }
    mid := (l + r) / 2
    left := mergeRange(lists, l, mid)
    right := mergeRange(lists, mid+1, r)
    return mergeTwoLists(left, right)
}

func mergeTwoLists(l1, l2 *ListNode) *ListNode {
   
    dummy := &ListNode{
   }
    curr := dummy

    for l1 != nil && l2 != nil {
   
        if l1.Val < l2.Val {
   
            curr.Next = l1
            l1 = l1.Next
        } else {
   
            curr.Next = l2
            l2 = l2.Next
        }
        curr = curr.Next
    }

    if l1 != nil {
   
        curr.Next = l1
    } else {
   
        curr.Next = l2
    }

    return dummy.Next
}

📊 解法对比 & 复杂度分析

解法 时间复杂度 空间复杂度 特点说明
✅ 最小堆 $O(n \log k)$ $O(k)$ 快速选择当前最小节点
✅ 分治合并 $O(n \log k)$ $O(\log k)$ 简洁高效,适合面试常考套路
  • 其中,$n$ 是所有链表节点总数,$k$ 是链表数量。

🧠 总结

  • 🔧 如果要求效率,最小堆优先,适合在线合并场景。
  • 📚 如果偏向代码整洁,可选择分治合并,便于拓展与理解。
  • 💡 重点掌握:链表合并逻辑 + 堆操作 + 分治模板。

✅ 如果你觉得这篇文章对你有帮助,欢迎点赞 👍、收藏 ⭐、关注 💻,我会持续更新更多高质量 LeetCode 热题详解!🚀📌🎯


目录
相关文章
|
6天前
|
Go 开发者 索引
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣33 / 81/ 153/154)(Go语言版)
本文深入探讨了LeetCode中四道关于「搜索旋转排序数组」的经典题目,涵盖了无重复和有重复元素的情况。通过二分查找的变形应用,文章详细解析了每道题的解题思路和Go语言实现代码。关键点包括判断有序区间、处理重复元素以及如何缩小搜索范围。文章还总结了各题的异同,并推荐了类似题目,帮助读者全面掌握二分查找在旋转数组中的应用。无论是初学者还是有经验的开发者,都能从中获得实用的解题技巧和代码实现方法。
37 14
|
6天前
|
存储 算法 Go
【LeetCode 热题100】17:电话号码的字母组合(详细解析)(Go语言版)
LeetCode 17题解题思路采用回溯算法,通过递归构建所有可能的组合。关键点包括:每位数字对应多个字母,依次尝试;递归构建下一个字符;递归出口为组合长度等于输入数字长度。Go语言实现中,使用map存储数字到字母的映射,通过回溯函数递归生成组合。时间复杂度为O(3^n * 4^m),空间复杂度为O(n)。类似题目包括括号生成、组合、全排列等。掌握回溯法的核心思想,能够解决多种排列组合问题。
34 11
|
4天前
|
Go
【LeetCode 热题100】155:最小栈(详细解析)(Go语言版)
本文详细解析了力扣热题155:最小栈的解题思路与实现方法。题目要求设计一个支持 push、核心思路是使用辅助栈法,通过两个栈(主栈和辅助栈)来维护当前栈中的最小值。具体操作包括:push 时同步更新辅助栈,pop 时检查是否需要弹出辅助栈的栈顶,getMin 时直接返回辅助栈的栈顶。文章还提供了 Go 语言的实现代码,并对复杂度进行了分析。此外,还介绍了单栈 + 差值记录法的进阶思路,并总结了常见易错点,如 pop 操作时忘记同步弹出辅助栈等。
23 6
|
6天前
|
Go 索引
【LeetCode 热题100】739:每日温度(详细解析)(Go语言版)
这篇文章详细解析了 LeetCode 第 739 题“每日温度”,探讨了如何通过单调栈高效解决问题。题目要求根据每日温度数组,计算出等待更高温度的天数。文中推荐使用单调递减栈,时间复杂度为 O(n),优于暴力解法的 O(n²)。通过实例模拟和代码实现(如 Go 语言版本),清晰展示了栈的操作逻辑。此外,还提供了思维拓展及相关题目推荐,帮助深入理解单调栈的应用场景。
34 6
|
1月前
|
Go
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣437 / 236 )(Go语言版)
本文深入探讨二叉树中路径与祖先问题,涵盖两道经典题目:LeetCode 437(路径总和 III)和236(最近公共祖先)。对于路径总和 III,文章分析了双递归暴力解法与前缀和优化方法,后者通过哈希表记录路径和,将时间复杂度从O(n²)降至O(n)。在最近公共祖先问题中,采用后序遍历递归查找,利用“自底向上”的思路确定最近公共祖先节点。文中详细解析代码实现与核心要点,帮助读者掌握深度追踪技巧,理解树结构中路径与节点关系的本质。这类问题在面试中高频出现,掌握其解法意义重大。
55 4
|
1月前
|
存储 算法 物联网
解析局域网内控制电脑机制:基于 Go 语言链表算法的隐秘通信技术探究
数字化办公与物联网蓬勃发展的时代背景下,局域网内计算机控制已成为提升工作效率、达成设备协同管理的重要途径。无论是企业远程办公时的设备统一调度,还是智能家居系统中多设备间的联动控制,高效的数据传输与管理机制均构成实现局域网内计算机控制功能的核心要素。本文将深入探究 Go 语言中的链表数据结构,剖析其在局域网内计算机控制过程中,如何达成数据的有序存储与高效传输,并通过完整的 Go 语言代码示例展示其应用流程。
40 0
|
8月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
117 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
9月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
98 6
|
9月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
203 2
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
173 1