【力扣算法】23-合并k个排序链表

本文探讨了合并多个已排序链表的有效算法,包括使用最小堆、优先队列及分治法实现的K路归并策略。通过对比不同方法的执行效率,分析了其时间和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

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

示例:

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

题解

无官方题解

一开始想要自己实现一个胜者组最小堆k路归并算法,感觉短时间实现不出胜者组最小堆的细节。

于是想参考Java优先队列(PriorityQueue)示例 使用Java类库中的优先队列来做一个最小堆k路归并算法。但后来自己瞎分析一下,感觉这样弄,复杂度好像是O(nlogk),后来自己想了个歪理:logn=log(k*n/k)=logk+log(n/k),除非是每个链表特别长,导致n/k特别大,那效果会不会差不多?还不如直接讨巧把全部节点读出来,然后一起排,O(nlogn)… 代码好写些

真正跑起来发现O(nlogn)和O(nlogk)还是有差距:

执行用时 : 21 ms, 在Merge k Sorted Lists的Java提交中击败了55.55% 的用户

内存消耗 : 43.6 MB, 在Merge k Sorted Lists的Java提交中击败了60.51% 的用户

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        
        ArrayList<ListNode> al = new ArrayList<>();
        
        for (ListNode head : lists) {
            while (head != null) {
                al.add(head);
                head=head.next;
            }
        }
        al.sort(new Comparator<ListNode>() {
            public int compare(ListNode o1, ListNode o2) {
                if(o1.val==o2.val)return 0;//不加这句的话,130/131例子过不去,是一个数组含有大量单节点链表的例子
                else return o1.val < o2.val ? -1 : 1;
            }
        });
        ListNode head = new ListNode(-1);
        if(al.size()!=0){
            head.next=al.get(0);
            int pIter = 1;

            while (pIter<al.size()) {
                al.get(pIter-1).next=al.get(pIter);
                pIter++;
            }
        }
        head = head.next;
        return head;
    }    
}

编写过程中还碰到一个错误:“Line 15: java.lang.IllegalArgumentException: Comparison method violates its general contract!

后来查了一下貌似是没有Comparator没有加等于的情况导致的?参考 加了注释那一行就过了。

至于最小堆的实现方法,自己最后还是参考K路归并问题小结 把答案写出来的。顺便学习了一下分治法的实现(分治法还快一些。。。)

最小堆k路归并(使用Java类库PriorityQueue):

执行用时 : 16 ms, 在Merge k Sorted Lists的Java提交中击败了64.03% 的用户

内存消耗 : 40.5 MB, 在Merge k Sorted Lists的Java提交中击败了82.81% 的用户

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        
        PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length, new Comparator<ListNode>() {
            public int compare(ListNode o1, ListNode o2) {
                return o1.val < o2.val ? -1 : 1;
            }
        });
        
        for (ListNode head : lists) {
            if (head != null) {
                pq.offer(head);
            }
        }
        ListNode head = new ListNode(-1);
        ListNode pIter = head;
        while (!pq.isEmpty()) {
            ListNode min = pq.poll();
            if (min.next != null) {
                pq.offer(min.next);
            }
            ListNode pNew = new ListNode(min.val);
            pIter.next = pNew;
            pIter = pIter.next;
        }
        head = head.next;
        return head;
    }    
}

分治法k路归并:

执行用时 : 9 ms, 在Merge k Sorted Lists的Java提交中击败了85.12% 的用户

内存消耗 : 44.2 MB, 在Merge k Sorted Lists的Java提交中击败了53.57% 的用户

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        return kMergeSort(lists, 0, lists.length - 1);
    }
    
    private ListNode kMergeSort(ListNode[] lists, int start, int end) {
        if (start >= end) {
            return lists[start];
        }
        int mid = start + (end - start) / 2;
        ListNode left = kMergeSort(lists, start, mid);
        ListNode right = kMergeSort(lists, mid + 1, end);
        return mergeTwoLists(left, right);
    }

    private ListNode mergeTwoLists(ListNode left, ListNode right) {
        if (left == null) {
            return right;
        }
        if (right == null) {
            return left;
        }
        
        ListNode head = new ListNode(-1);
        // pIter始终指向新节点
        ListNode pIter = head;
        ListNode p1 = left, p2 = right;
        while (p1 != null && p2 != null) {
            if (p1.val < p2.val) {
                pIter.val = p1.val;
                p1 = p1.next;
            } else {
                pIter.val = p2.val;
                p2 = p2.next;
            }
            ListNode pNew = new ListNode(-1);
            pIter.next = pNew;
            pIter = pIter.next;
        }
        while (p1 != null) {
            pIter.val = p1.val;
            if (p1.next != null) {
                ListNode pNew = new ListNode(-1);
                pIter.next = pNew;
                pIter = pIter.next;
            }
            p1 = p1.next;
        }
        while (p2 != null) {
            pIter.val = p2.val;
            if (p2.next != null) {
                ListNode pNew = new ListNode(-1);
                pIter.next = pNew;
                pIter = pIter.next;
            }
            p2 = p2.next;
        }
        return head;
    }    
}

感想

数据结构还是学的烂呀,抽时间去看书!!!

用分治法实现K路归并的方法还是可以学习一下的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值