合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
题解:version -1:分治
- 参照希尔排序的思想,将K个链表两两归并,可以使用递归
- 这里使用迭代:双指针left指向待排数组头,right指向待排数组尾
- 将left、right所指的两个链表进行合并,合并后的结果存放在left中
- 已经比较过的链表丢弃(即right向前一半)
- 数组长度为1时停止比较,第一个元素即为合并后的链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int len = lists.length;
while(len>1){
int left = 0, right = len-1;
while(left<right){
ListNode mergeTwo = mergeTwoLists(lists[left], lists[right]);
lists[left] = mergeTwo;//left和right合并的结果保存在left中
left++;
right--;
}
len = len-(len/2);//每次去除lists后半段已经比较过的
}
return len==0?null:lists[0];
}
/*二路归并*/
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode(-1);
head.next = l1;//临时头结点
ListNode c = head;//哨兵头结点
ListNode p = l1;
ListNode q = l2;
while(p!=null && q!=null){
if(q.val < p.val){
c.next = q;
q = q.next;
}else{
c.next = p;
p = p.next;
}
c = c.next;
}
if(p!=null){
c.next = p;
}
if(q!=null){
c.next = q;
}
return head.next;
}
}
version -2:堆排序
- 建立最小堆
- 在建堆的时候,每插入一个元素,从堆尾递归上滤一次(未插入前已满足堆的属性)
- 在去堆建链表的时候,每删除一个元素,从堆顶递归下滤一次
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
List<ListNode> ret = new ArrayList<>();
void goUp(int k){//插入时递归上滤
if(k<=1){
return ;
}
if(ret.get(k).val<ret.get(k/2).val){//与父结点比较,上滤
ListNode tmp = ret.get(k/2);
ret.set(k/2,ret.get(k));
ret.set(k,tmp);
goUp(k/2);
}
}
void goDown(int k){//删除时递归下滤
int left = k*2, right=k*2 +1,min=k;
if(left<ret.size() && ret.get(left).val<ret.get(min).val){
min = left;
}
if(right<ret.size() && ret.get(right).val<ret.get(min).val){
min = right;
}
if(min != k){//与子节点比较,下滤
ListNode tmp = ret.get(min);
ret.set(min,ret.get(k));
ret.set(k,tmp);
goDown(min);
}
}
ListNode buildList(){//输出链表
ListNode head = ret.get(1);
ret.set(1,ret.get(ret.size()-1));
ret.remove(ret.size()-1);//取出目前下标为1最小的结点并将最后一个结点置于下标为1的位置,删除最后一个空位
goDown(1);//下滤
return head;
}
public ListNode mergeKLists(ListNode[] lists) {
/**
最小堆完成K路归并
*/
if(lists.length == 0){
return null;
}
ret.add(new ListNode(-1));//下标为0结点设空
for(int i=0 ; i<lists.length ; i++){//录入结点
ListNode cur = lists[i];
while(cur!=null){
ret.add(cur);
goUp(ret.size()-1);//每插入一次上滤一次,必须在前面已经满足堆的情况下才可以只递归处理当前元素
cur=cur.next;
}
}
if(ret.size()==1){
return null;
}
ListNode head = new ListNode(-1);
ListNode p = head;
while(ret.size() >= 2){//剩余下标0的空结点和下标1的最后一个结点
p.next = buildList();
p = p.next;
}
p.next = null;
return head.next;
}
}