LeetCode | Merge k Sorted Lists

https://leetcode.com/problems/merge-k-sorted-lists/?tab=Description

思路1:暴力合并

一个简单的做法是遍历这k个链表,不断调用merge two lists的方法依次合并。这种做法效率很低。

Time complexity: 假设所有的链表长度都是n,那么时间复杂度是O(n + 2n + ... + kn) = O(k^2 n), 其中k是链表个数。

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *ret = NULL;
        for (int i = 0; i < lists.size(); ++i) {
            ret = merge_two_lists(ret, lists[i]);
        }
        return ret;
    }
    
    ListNode* merge_two_lists(ListNode* l1, ListNode* l2) {
        ListNode dummy(0), *cur = &dummy;
        while (l1 && l2) {
            if (l1->val < l2->val) { cur->next = l1; l1 = l1->next; }
            else                   { cur->next = l2; l2 = l2->next; }
            cur = cur->next;
        }
        cur->next = l1 ? l1 : l2;
        return dummy.next;
    }
};

思路2:分治法

把vector分成左、右两部分,然后分别对左、右两边做mergeKLists,最后合并左、右的有序链表。和归并排序如出一辙。

Time complexity: O(N log k)。其中k是链表个数,N是合并完k个链表后的总长度。
Space complexity: O(1)

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.size() == 0) return NULL;
        return helper(lists, 0, lists.size() - 1);
    }
private:
    ListNode* helper(vector<ListNode*>& lists, int start, int end) {
        if (start == end) return lists[start];
        int mid = start + (end - start) / 2;
        ListNode *left = helper(lists, start, mid);
        ListNode *right = helper(lists, mid + 1, end);
        return merge_two_lists(left, right);
    }
    
    ListNode* merge_two_lists(ListNode* l1, ListNode* l2) {
        ListNode dummy(0), *cur = &dummy;
        while (l1 && l2) {
            if (l1->val < l2->val) { cur->next = l1; l1 = l1->next; }
            else                   { cur->next = l2; l2 = l2->next; }
            cur = cur->next;
        }
        cur->next = l1 ? l1 : l2;
        return dummy.next;
    }
};

思路3:优先队列

  1. 创建一个优先队列(或最小堆),遍历所有链表,把链表节点推入优先队列;
  2. 不断从优先队列中弹出所有节点,同时将它们按弹出的先后顺序连接起来,直到队列空,最后得到的结果就是合并后的有序链表。

Time complexity: O(N log N)

Space complexity: O(N). 其中N是所有链表节点总数

C++

struct Cmp {
    bool operator()(const ListNode* a, const ListNode* b) const {
        return a->val > b->val;
    }
};

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int n = lists.size();
        if (n == 0) return NULL;
        if (n == 1) return lists[0];
        
        priority_queue<ListNode*, vector<ListNode*>, Cmp> pq;
        for (int i = 0; i < n; ++i) {
            ListNode *head = lists[i];
            while (head) {
                pq.push(head);
                head = head->next;
            }
        }
        
        ListNode dummy(0), *cur = &dummy;
        while (!pq.empty()) {
            cur->next = pq.top(); pq.pop();
            cur = cur->next;
        }
        cur->next = NULL;  // very important!
        
        return dummy.next;
    }
};

在上面的代码中,注意倒数第二句cur->next = NULL千万不能漏,因为最后弹出的节点不一定就是某个链表的尾节点,不加上这句的话,就会导致得到的链表有环。

思路4:两两合并

记下链表的总数,设为n

  1. 合并lists[0]和lists[1]并覆盖lists[0],合并lists[2]和lists[3]并覆盖lists[2],...注意如果n是偶数,正好可以全部两两合并完,即最后合并的是lists[n-2]和lists[n-1](合并完覆盖lists[n-2 / 2]);而如果n是奇数,lists[n-1]会“落单”,这时处理方法是直接将lists[n-1]去覆盖lists[n-1 / 2]。
  2. 更新n: n = ceil(n / 2.0) 为什么是向上取整?举个例子就清楚了:比如n一开始等于4,第一轮合并后应该变为2个,而如果n一开始等于5,第一轮合并后最后一个落单,所以变为3个。
  3. 重复步骤1、2,直到n为1
  4. 返回lists[0]

时间和空间复杂度同分治法。

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int n = lists.size();
        if (n == 0) return NULL;
        if (n == 1) return lists[0];
        
        while (n > 1) {
            for (int i = 0; i < n; i += 2) {
                if (i != n - 1) {
                    lists[i / 2] = merge_two_lists(lists[i], lists[i+1]);
                } else {
                    lists[i / 2] = lists[i];
                }
            }
            n = ceil(n / 2.0);
        }
        
        return lists[0];
    }
private:
    ListNode* merge_two_lists(ListNode* l1, ListNode* l2) {
        ListNode dummy(0), *cur = &dummy;
        while (l1 && l2) {
            if (l1->val < l2->val) { cur->next = l1; l1 = l1->next; }
            else                   { cur->next = l2; l2 = l2->next; }
            cur = cur->next;
        }
        cur->next = l1 ? l1 : l2;
        return dummy.next;
    }
};
原文地址:https://www.cnblogs.com/ilovezyg/p/6384604.html