【LeetCode题解】23. 合并K个排序链表

合并 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;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值