算法题学习---合并k个已排序的链表

81 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

题目

描述

合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。

数据范围:节点总数0≤n≤5000,每个节点的val满足 ∣val∣<=1000

要求:时间复杂度 O(nlogn)

示例1

输入:
[{1,2,3},{4,5,6,7}]
返回值:
{1,2,3,4,5,6,7}

示例2

输入:
[{1,2},{1,4,5},{6}]
返回值:
{1,1,2,4,5,6}

分析

考虑特殊情况

在进行问题分析过程前,我们先考虑一下特殊情况,那么在这道问题中,有哪些情况是特殊的呢?

不难发现,如下情况:

  • 如果输入的链表数量为0,那么直接返回空指针即可;
  • 如果输入的链表数量为1,那么直接返回该链表即可;

考虑一般情况

其实这个问题是合并两个链表的问题衍生,我们之前学习了如何合并两个有序链表,那么我们可以将这个问题进行拆解。

第一种思路,也是最容易想到的思路,对所有链表依次进行合并:

先合并第一个,第二个,得到合并后的链表;然后使用该链表来和第三个链表进行合并,依次进行。

我们假设有n个链表,每两个链表合并平均需要遍历m次,那么我们的时间复杂度就大致应该是O(mn);

第二种思路,我们也是基于两个两个链表合并的函数,对我们拥有的n个链表进行拆解:

分为前半部分的链表和后半部分的链表,那最终的结果实际上是合并这两部分链表的结果;

前半部分和后半部分其实是我们的子问题,可以归并的方式获取;

最后的停止条件是我们的子区间为空返回空指针或者只有一个链表,返回该链表。

使用归并的方法就可以将使用两个链表合并操作的次数下降到O(logn),可以满足题目的要求。

代码示例

版本一

注意:这里pReturn首先赋值为lists[0],进行比较时从lists[1]开始

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
  public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0){
            return nullptr;
        }
        if(lists.size() == 1){
            return lists[0];
        }
        ListNode* pReturn = lists[0];
        for(size_t i = 1;i < lists.size();++i){
            pReturn = merge2Lists(pReturn, lists[i]);
        }
        return pReturn;
    }
    ListNode* merge2Lists(ListNode* pHead1, ListNode* pHead2) {
        // special condition
        if (nullptr == pHead1) {
            return pHead2;
        }

        if (nullptr == pHead2) {
            return pHead1;
        }

        ListNode* pReturn = nullptr;
        ListNode* pCur0 = nullptr;
        ListNode* pCur1 = pHead1;
        ListNode* pCur2 = pHead2;

        // assign the pReturn
        if (pHead1->val <= pHead2->val) {
            pReturn = pHead1;
            pCur1 = pCur1->next;
        } else {
            pReturn = pHead2;
            pCur2 = pCur2->next;
        }
        pCur0 = pReturn;

        // traverse the two list and link them
        while (nullptr != pCur1 && nullptr != pCur2) {
            if (pCur1->val <= pCur2->val) {
                pCur0->next = pCur1;
                pCur1 = pCur1->next;
            } else {
                pCur0->next = pCur2;
                pCur2 = pCur2->next;
            }
            pCur0 = pCur0->next;
        }

        if (nullptr == pCur1) {
            pCur0->next = pCur2;
        } else if (nullptr == pCur2) {
            pCur0->next = pCur1;
        }

        return pReturn;
    }
};

版本二

注意这里的终止条件:

left > right:返回nullptr

left == right:返回lists[left]

class Solution {
  public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0){
            return nullptr;
        }
        if(lists.size() == 1){
            return lists[0];
        }
        
        return divideMerge(lists, 0, lists.size() - 1);
    }
    ListNode* divideMerge(vector<ListNode*>& lists,int left, int right){
        if(left > right){
            return nullptr;
        }else if(left == right){
            return lists[left];
        }
        int mid = (left+right)/2;
        return merge2Lists(divideMerge(lists, left, mid), divideMerge(lists, mid+1, right));
    }
    ListNode* merge2Lists(ListNode* pHead1, ListNode* pHead2) {
        ...// 实现同1
    }
};