两个以及K个有序链表合并

LeetCode21. 合并两个有序链表(难度:简单)

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

思路:

两个有序链表的合并的思想很简单,分别设置两个指针,从两个链表的第一个节点开始比较大小,将值小的节点放到新的链表中,依次进行,直到有一个链表为空,此时,再将剩下的部分连接到已生成的链表后面即可。直接上代码:

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        /*新建一个头结点以存放新生成的链表*/
        ListNode l3 = new ListNode(0);
        ListNode cur = l3;
        /*当两个链表都不为空,则依次进行比较*/
        while(l1!=null&&l2!=null){
            if(l1.val<l2.val){
                cur.next = l1;
                l1 = l1.next;
            }else{
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        /*有一条为空,则将剩下的链表连接到已生成的新链表后边*/
        if(l1==null){
            cur.next = l2;
        }
        if(l2==null){
            cur.next = l1;
        }
        /*返回新链表的第一个节点*/
        return l3.next;
    }
}

LeetCode23. 合并K个有序链表(难度:困难)

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

思路一:

其实做完两个有序链表的合并之后来做这题,总体思路是一致的,就是细节上会有所差异。我们需要每次从这K个链表中找到一个最小的(假设是从小到大),然后连接到新链表的尾部(尾插法),直至把K条链表上所有的节点都插入完成结束。

这里有一个点需要注意,没法像两个链表那样,在一条为空后,直接把另外一条链表插入到新链表的尾部,因为这里有K条,其实剩余的K-1条还需要继续进行比较。知道这个注意点之后,直接上代码:

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        /*新建一个头结点以存放新生成的链表*/
        ListNode h = new ListNode(0);
        ListNode cur = h;

        /*在没有遍历完所有节点之前,一直循环*/
        while(true){
            ListNode minNode = null; // 最小值节点
            int min = -1;  //下标,用于标记是第几条链表的
            for(int i=0;i<lists.length;i++){
                if(lists[i]==null){
                    continue;  //如果某一条链表已经被遍历完了,则跳过下面的操作
                }
                // 比较,找到最小的节点
                if(minNode==null||lists[i].val<minNode.val){
                    minNode=lists[i];
                    min = i;
                }
            }
            /*这个if语句很重要,是判断K条链表是否所有的节点都已经被遍历完,不加这条,则无法跳出循环*/
            if(min==-1) {
                break;
            }
            /*尾插法插入到新链表*/
            cur.next = minNode;
            cur = cur.next;
            /*这一步也很重要,别忘了更新节点位置*/
            lists[min] = lists[min].next;
        }
        return h.next;
    }
}

时间复杂度:O(NK)

思路二:

既然每次都是要取一个最小值节点,这肯定会让我们想到一个数据结构,堆。我们可以使用小顶堆来对思路一的代码进行优化。在Java的库里边我们则可以使用优先队列PriorityQueue。如果有同学对优先队列不了解的,可以访问廖雪峰的Java基础教程。那么我们就对上述代码进行改进:

  1. 首先我们要创建一个优先队列,并且把K条链表的第一个节点存放到队列中。注意这里为了代码的简洁,使用了Java8新特性Lambda表达式.
Queue<ListNode> q = new PriorityQueue<ListNode>((a,b)-> a.val-b.val);
for(int i=0;i<lists.length;i++){
    if(lists[i]!=null){
        q.offer(lists[i]);
    }
}
  1. 对循环的条件和内部进行改进
while(!q.isEmpty()){
	/*每次从优先队列取出最小值节点*/
    ListNode minNode = q.poll();
    /*尾插法*/
    cur.next = minNode;
    cur = cur.next;
    /*这一步的作用相当于思路一种的 更新指针操作,如果还有后续节点,则需要放入到优先队列中*/
    if(minNode.next!=null){
        q.offer(minNode.next);
    }
}

完整代码:

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode h = new ListNode(0);
        ListNode cur = h;
        Queue<ListNode> q = new PriorityQueue<ListNode>((a,b)-> a.val-b.val);
        for(int i=0;i<lists.length;i++){
            if(lists[i]!=null){
                q.offer(lists[i]);
            }
        }
        while(!q.isEmpty()){
            ListNode minNode = q.poll();
            cur.next = minNode;
            cur = cur.next;
            if(minNode.next!=null){
                q.offer(minNode.next);
            }
        }
        return h.next;
    }
}

时间复杂度:O(NLogK)

这是本人对有序链表合并的记录总结。看完这两道有序链表的题目,小伙伴们的思路有没有清晰一点呢?希望对你们有所帮助!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值