数据结构与算法之深入解析“合并K个升序链表”的求解思路与算法示例

Posted Serendipity·y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法之深入解析“合并K个升序链表”的求解思路与算法示例相关的知识,希望对你有一定的参考价值。

一、题目要求

  • 给你一个链表数组,每个链表都已经按升序排列。
  • 请你将所有链表合并到一个升序链表中,返回合并后的链表。
  • 示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
  • 示例 2:
输入:lists = []
输出:[]
  • 示例 3:
输入:lists = [[]]
输出:[]
  • 提示:
    • k == lists.length
    • 0 <= k <= 104
    • 0 <= lists[i].length <= 500
    • -104 <= lists[i][j] <= 104
    • lists[i] 按升序排列
    • lists[i].length 的总和不超过 104

二、求解算法

① 顺序合并

  • 可以想到一种最朴素的方法:用一个变量 ans 来维护以及合并的链表,第 i 次循环把第 i 个链表和 ans 合并,答案保存到 ans 中。
  • C++ 示例:
class Solution 
public:
    ListNode* mergeTwoLists(ListNode *a, ListNode *b) 
        if ((!a) || (!b)) return a ? a : b;
        ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
        while (aPtr && bPtr) 
            if (aPtr->val < bPtr->val) 
                tail->next = aPtr; aPtr = aPtr->next;
             else 
                tail->next = bPtr; bPtr = bPtr->next;
            
            tail = tail->next;
        
        tail->next = (aPtr ? aPtr : bPtr);
        return head.next;
    

    ListNode* mergeKLists(vector<ListNode*>& lists) 
        ListNode *ans = nullptr;
        for (size_t i = 0; i < lists.size(); ++i) 
            ans = mergeTwoLists(ans, lists[i]);
        
        return ans;
    
;
  • Java 示例:
class Solution 
    public ListNode mergeKLists(ListNode[] lists) 
        ListNode ans = null;
        for (int i = 0; i < lists.length; ++i) 
            ans = mergeTwoLists(ans, lists[i]);
        
        return ans;
    

    public ListNode mergeTwoLists(ListNode a, ListNode b) 
        if (a == null || b == null) 
            return a != null ? a : b;
        
        ListNode head = new ListNode(0);
        ListNode tail = head, aPtr = a, bPtr = b;
        while (aPtr != null && bPtr != null) 
            if (aPtr.val < bPtr.val) 
                tail.next = aPtr;
                aPtr = aPtr.next;
             else 
                tail.next = bPtr;
                bPtr = bPtr.next;
            
            tail = tail.next;
        
        tail.next = (aPtr != null ? aPtr : bPtr);
        return head.next;
    

② 分治合并

  • 将 k 个链表配对并将同一对中的链表合并;
  • 第一轮合并以后, k 个链表被合并成了 k/2 个链表,平均长度为 2n/k,然后是 k/4 个链表, k/8 个链表等;
  • 重复这一过程,直到得到了最终的有序链表。

  • C++ 示例:
class Solution 
public:
    ListNode* mergeTwoLists(ListNode *a, ListNode *b) 
        if ((!a) || (!b)) return a ? a : b;
        ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
        while (aPtr && bPtr) 
            if (aPtr->val < bPtr->val) 
                tail->next = aPtr; aPtr = aPtr->next;
             else 
                tail->next = bPtr; bPtr = bPtr->next;
            
            tail = tail->next;
        
        tail->next = (aPtr ? aPtr : bPtr);
        return head.next;
    

    ListNode* merge(vector <ListNode*> &lists, int l, int r) 
        if (l == r) return lists[l];
        if (l > r) return nullptr;
        int mid = (l + r) >> 1;
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
    

    ListNode* mergeKLists(vector<ListNode*>& lists) 
        return merge(lists, 0, lists.size() - 1);
    
;
  • Java 示例:
class Solution 
    public ListNode mergeKLists(ListNode[] lists) 
        return merge(lists, 0, lists.length - 1);
    

    public ListNode merge(ListNode[] lists, int l, int r) 
        if (l == r) 
            return lists[l];
        
        if (l > r) 
            return null;
        
        int mid = (l + r) >> 1;
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
    

    public ListNode mergeTwoLists(ListNode a, ListNode b) 
        if (a == null || b == null) 
            return a != null ? a : b;
        
        ListNode head = new ListNode(0);
        ListNode tail = head, aPtr = a, bPtr = b;
        while (aPtr != null && bPtr != null) 
            if (aPtr.val < bPtr.val) 
                tail.next = aPtr;
                aPtr = aPtr.next;
             else 
                tail.next = bPtr;
                bPtr = bPtr.next;
            
            tail = tail.next;
        
        tail.next = (aPtr != null ? aPtr : bPtr);
        return head.next;
    

③ 使用优先队列合并

  • 这个方法和前两种方法的思路有所不同,我们需要维护当前每个链表没有被合并的元素的最前面一个,k 个链表就最多有 k 个满足这样条件的元素,每次在这些元素里面选取 val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。
  • C++ 示例:
class Solution 
public:
    struct Status 
        int val;
        ListNode *ptr;
        bool operator < (const Status &rhs) const 
            return val > rhs.val;
        
    ;

    priority_queue <Status> q;

    ListNode* mergeKLists(vector<ListNode*>& lists) 
        for (auto node: lists) 
            if (node) q.push(node->val, node);
        
        ListNode head, *tail = &head;
        while (!q.empty()) 
            auto f = q.top(); q.pop();
            tail->next = f.ptr; 
            tail = tail->next;
            if (f.ptr->next) q.push(f.ptr->next->val, f.ptr->next);
        
        return head.next;
    
;
  • Java 示例:
class Solution 
    class Status implements Comparable<Status> 
        int val;
        ListNode ptr;

        Status(int val, ListNode ptr) 
            this.val = val;
            this.ptr = ptr;
        

        public int compareTo(Status status2) 
            return this.val - status2.val;
        
    

    PriorityQueue<Status> queue = new PriorityQueue<Status>();

    public ListNode mergeKLists(ListNode[] lists) 
        for (ListNode node: lists) 
            if (node != null) 
                queue.offer(new Status(node.val, node));
            
        
        ListNode head = new ListNode(0);
        ListNode tail = head;
        while (!queue.isEmpty()) 
            Status f = queue.poll();
            tail.next = f.ptr;
            tail = tail.next;
            if (f.ptr.next != null) 
                queue.offer(new Status(f.ptr.next.val, f.ptr.next));
            
        
        return head.next;
    

以上是关于数据结构与算法之深入解析“合并K个升序链表”的求解思路与算法示例的主要内容,如果未能解决你的问题,请参考以下文章

打卡算法23合并K个升序链表 算法解析

算法系列——合并K个升序链表

算法系列——合并K个升序链表

算法系列——合并K个升序链表

算法系列——合并K个升序链表

算法leetcode|23. 合并K个升序链表(rust重拳出击)