力扣23(java)-合并k个升序链表(困难)
题目:
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
参考@【王尼玛】的解题思路
一、小根堆
如图:4个链表中的最小值,一定来自黄色的部分,黄色的部分就是一个小根堆。
这个堆的元素个数是lists中链表的个数,初始时只是将每个链表的头结点放入堆中,建立完初始的堆后,就不断的从堆中获取节点,如果获取到的节点不为空,即还有下一个节点,那么就将下一个节点放到堆中。
代码:
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode() {} 7 * ListNode(int val) { this.val = val; } 8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 9 * } 10 */ 11 class Solution { 12 public ListNode mergeKLists(ListNode[] lists) { 13 if(lists == null || lists.length == 0) return null; 14 PriorityQueue<ListNode> queue = new PriorityQueue(new Comparator<ListNode>(){ 15 public int compare(ListNode l1, ListNode l2){ 16 //升序 17 return l1.val - l2.val; 18 } 19 }); 20 ListNode dummy = new ListNode(0); 21 ListNode cur = dummy; 22 //把每个链表的头结点放进队列中 23 for(ListNode node: lists){ 24 if(node != null){ 25 queue.add(node); 26 } 27 } 28 while(!queue.isEmpty()){ 29 cur.next = queue.poll(); 30 cur = cur.next; 31 //将当前结点的下一个结点也放入队列中 32 if(cur.next != null){ 33 queue.add(cur.next); 34 } 35 } 36 return dummy.next; 37 } 38 }
二、分治和合并
先将整个链表在中间进行拆分成两部分,然后再对这两部分继续拆分,直到拆分成最小单元(只有一个链表)时,再进行两两进行合并,合并后以当前合并后的链表为新的链表再与其他的链表进行合并。看图更容易理解
代码:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode mergeKLists(ListNode[] lists) { if(lists == null || lists.length == 0) return null; return mergeTwolists(lists, 0, lists.length - 1); } //分治 private ListNode mergeTwolists(ListNode[] lists, int start, int end){ if(start == end){ return lists[start]; } int mid = start + (end - start) / 2; ListNode l1 = mergeTwolists(lists, start, mid); ListNode l2 = mergeTwolists(lists, mid+1, end); return merge(l1, l2); } //合并 private ListNode merge(ListNode a, ListNode b){ if(a == null || b == null){ return (a == null) ? b : a; } if(a.val < b.val){ a.next = merge(a.next, b); return a; }else{ b.next = merge(a, b.next); return b; } } }
小知识:
1.compare(Object o1, Object o2)
方法,比较o1和o2的大小:
如果要按照升序排序,
则 o1小于o2返回 -1,o1与o2相等返回 0,01大于02返回 1
如果要按照降序排序
则 o1小于o2返回 1,o1与o2相等返回 0,01大于02返回 -1
2.优先队列:优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序
常用方法:
peek()//返回队首元素
poll()//返回队首元素,队首元素出队列
add()//添加元素
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false
优先队列中的元素可以默认自然排序
或者通过提供的Comparator(比较器)在队列实例化的时排序
。(不指定Comparator时默认为最小堆)
1 PriorityQueue<Integer> heap = new PriorityQueue<Integer>(new Comparator<Integer>() { 2 @Override 3 public int compare(Integer o1, Integer o2) { 4 //升序(小顶堆) 5 return o1 - o2; 6 或者这样写: 7 //降序(大顶堆) 8 return o2 - o1; 9 } 10 });
总结:
小顶堆, return
前减后(o1-o2);
大顶堆, return
后减前(o2-o1);
在堆中,比较器的使用方式为:若返回值>=0,则表示不要交换,否则,需要交换。所以我们只需要认清传入的两个参数,o1
和o2
哪一个是谁我们就可以控制其是否要交换,只需要记住,o1
永远是新的,在插入时是child
,删除时是parent。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)