写点什么

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

作者:桑榆
  • 2022-11-09
    广东
  • 本文字数:1908 字

    阅读完需:约 6 分钟

题目

描述

合并 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    }};
复制代码


发布于: 刚刚阅读数: 4
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020-02-29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
算法题学习---合并k个已排序的链表_算法题_桑榆_InfoQ写作社区