基础算法(Leetcode)刻意练习第十二天——链表

引言

由 “LSGO软件技术团队” 组织的 “基础算法刻意练习” 采用分类别练习的模式,即选择了五个知识点(数组、链表、字符串、树、贪心算法),每个知识点选择了 三个简单、两个中等、一个困难 等级的题目,共计三十道题,利用三十天的时间完成这组刻意练习。以下是我的每日打卡记录:


Task12.合并K个排序链表

  • Leecode第23题

  • 难度:困难

  • 题目概述:

      合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度
    
      示例:
      输入:
      		[
      		1->4->5,
      		1->3->4,
      		2->6
      		]
      输出: 1->1->2->3->4->4->5->6
    

题解思路


  • 1.逐一比较之"优先级队列"(参考题解)

利用优先级队列自动排序的特性,将 k 个第一个链表元素放入队列(自动排好序),弹出最小元素后,放入第二个元素,直到链表为空。

时间复杂度: O(Nlogk)
比较操作的代价会被队列优化到 O(logk) ( 本应是O(k) )。同时,找到最小值节点的时间开销仅仅为 O(1)。

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
		//建立升序的优先级队列
        ListNode dummyHead = new ListNode(0);
        ListNode curr = dummyHead;
        PriorityQueue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {
            public int compare(ListNode o1, ListNode o2) {
                return o1.val - o2.val;
            }
        });
		//将链表插入队列
        for (ListNode list : lists) {
            if (list == null) {
                continue;
            }
            pq.add(list);
        }
		//逐一弹出元素
        while (!pq.isEmpty()) {
            ListNode nextNode = pq.poll();
            curr.next = nextNode;
            curr = curr.next;
            if (nextNode.next != null) {
                pq.add(nextNode.next);
            }
        }
        return dummyHead.next;
    }
}

提交记录

基础算法(Leetcode)刻意练习第十二天——链表_第1张图片


  • 2.两两合并之"逐一合并"

合并 lists[0] + lists[1],然后与 lists[2] 合并,然后与 lists[3] 合并…直到合并完成,总共合并 k-1 次。

时间复杂度: O(kN)

/**
 * 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 n=lists.length;
        if(n==0) 
            return null;
        ListNode result=lists[0];
        for(int i=1;i<n;i++)
            result=mergeTwoLists(result,lists[i]);
        return result;
    }
	//第21题,合并两个有序链表的答案
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode res=new ListNode(0); 
        ListNode tmp=res; 
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                tmp.next=l1;
                l1=l1.next;
            }
            else{
                tmp.next=l2;
                l2=l2.next;
            }
            tmp=tmp.next;
        }
        tmp.next=(l1==null)?l2:l1;
        return res.next;
    }
}

提交记录

由于调用了无数次方法,时间很慢
基础算法(Leetcode)刻意练习第十二天——链表_第2张图片


  • 3.两两合并之"分治思想"

同样是两两合并,只不过这次不连续合并,而是头和尾合并,依次合并至中间。这样不仅减少了很多次重复的比较,每轮合并都将链表数组长度减少一半。

时间复杂度:O(Nlogk)

/**
 * 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;
        if (len == 0) {
            return null;
        }    
        // 将数组从中间分割,首位依次合并
        while(len>1) {
            for (int i=0; i<len/2; i++) {
                lists[i] = mergeTwoLists(lists[i], lists[len-1-i]);
            }
            len = (len+1)/2;
        }
        return lists[0];
    }
	//第21题,合并两个有序链表的答案
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode res=new ListNode(0); 
        ListNode tmp=res;
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                tmp.next=l1;
                l1=l1.next;
            }
            else{
                tmp.next=l2;
                l2=l2.next;
            }
            tmp=tmp.next;
        }
        tmp.next=(l1==null)?l2:l1;
        return res.next;
    }
}

提交记录

基础算法(Leetcode)刻意练习第十二天——链表_第3张图片


你可能感兴趣的:(基础算法(Leetcode)刻意练习第十二天——链表)