力扣23题合并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 = [[]]
输出:[]
两种思路比较好,归并的递归确实太难写了。
第一种就是将n个链表合并为一个链表可以分解为两个链表合并为一个链表的子问题,即第一个链表和第二个链表合并,将合并的链表和第三个链表合并,依次类推,直到最后两个链表合并即可。
两个链表合并的代码如下:
public ListNode appendNode(ListNode node1,ListNode node2){
//如果存在某个链表为空,则直接返回另外一个链表
//并且包括了两个链表都是空表的情况
if(node1==null || node2==null){
return node1!=null?node1:node2;
}
ListNode head = new ListNode();
ListNode ln = head;//指向表头为head的链表的尾部,目前尾部是head
while(node1!=null&&node2!=null){
if(node1.val>= node2.val){
ln.next = node2;//将node2连接到head的表尾
ln = ln.next;//ln指向表尾
node2 = node2.next;//node2表指向下一个节点
}else{
ln.next = node1;//将node1连接到head的表尾
ln = ln.next;//ln指向表尾
node1 = node1.next;//node1表指向下一个节点
}
}
//这个时候已经有一个表为空表了
ln.next = node1!=null?node1:node2;
return head.next;
}
}
第二种就是使用优先队列,我个人认为这是一种对堆排序的优化方法。
例如,可以将所有节点都存入优先队列中,然后依次出来,每次出来的都是队列中val最小的,那么就是升序。但是优先队列中元素越多,那么每次插入的时间复杂度越高O(logK),K是优先队列中的元素个数。(优先队列的底层是用堆这种数据结构实现的)
那么可以用第一种优化,就是用可变数组来保存节点,ArrayList,保存完所有节点再排一下序,再对排序的节点进行构造链表即可。这种第一次保存O(n),排序,平均时间复杂度约O(nlogn),最坏肯定是O(n方),再构造新表又是O(n)。空间复杂度就是n个节点O(n)。
那么再一步优化就是优先队列维护当前K个链表的最小值,即K个链表的表头。可以把每个链表看成一根一根管子,每根管子的头部都是最小的元素,那么优先队列只需要保存每根管子的头部,就是K个元素,然后每一次出去的是最小的元素,然后再更新优先队列,将这一次出去元素的链表的下一个元素加入到优先队列中。
假设现在有3个链表:1,2,3。优先队列pq放入这3个链表1,2,3
然后pq弹出元素,一定是弹出最小的元素,假设现在最小的元素在链表1中,那么接着就要更新链表1的表头元素入队列。这样一次一次地弹出最小的元素,直到优先队列所有元素全部弹出。
其实这种方法用到了每行元素有序,如果无序,不能这样弹出。因为下一个元素不一定比上一个元素大,按照弹出的顺序就不对了。
代码如下:
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists == null) return null;//空表
Comparator<ListNode> comparator = new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val-o2.val;
}
};
//优先队列,维护每个链表表头最小的值
PriorityQueue<ListNode> pq = new PriorityQueue<>(comparator);
ListNode head = new ListNode();
ListNode ln = head;
//将K个链表入优先队列
for(ListNode node:lists){
if(node!=null) pq.offer(node);
}
while (!pq.isEmpty()){
ListNode node = pq.poll();//弹出的是最小的链表节点
ln.next = node;
ln = ln.next;
//再将弹出节点的链表的下一个节点入队列
if(node.next!=null){
pq.offer(node.next);
}
}
return head.next;
}
}