[LeetCode] 23. Merge k Sorted Lists

本题是来自LeetCode上面一道难度为Hard的题,目的是将给定的k个已排序的列表最终融合成一个有序列表。本题的AC率还是可以的,有30%,但是看过讨论区发现,其实大多数人(使用cpp语言的)都是用priority_queue来做的。想到本周算法课刚刚学了分治算法中的归并排序与本题十分类似,因此我决定用归并的做法来做,并且这样做不需要用到除vector的额外数据结构。

一、问题描述

这里写图片描述

二、问题分析

本题的描述和列子都很清晰简洁。根据题目描述,输入应该是n个已排序的链表,然后要求把这些链表融合成一个最终排序好的链表并输出。拿到本题,我们想的肯定是先开辟一个存储区存储最终的融合结果。因为给定的链表都是已输入的,那么我们想要找到其中最小的数放到存储区只需要选出各个链表头元素中的最小元素即可。然而,这样做的效率并不高。假设一开始了K个链表,每个链表有N个数字。那么选出K个元素中的最小值的复杂度是K,然后要这么选K*N次,那么复杂度应该为 NK2 N K 2 。因此,本题的关键是,如何降低复杂度。由于本周个在算法课上学习了分治算法,以及其中的归并算法,再联系到本题的情景,发现正好可以用得上。

三、问题求解

第一个方法就是归并算法。主函数中把给定的链表两两一组进行合并,最终合并到一个链表时进行输出。

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.empty()) {
            return NULL;
        }
        while (lists.size() > 1) {
            for (int i = 0, j = 1; i < lists.size() && j < lists.size(); i++, j++) {
                lists[i] = mergeTwoLists(lists[i], lists[j]);
                lists.erase(lists.begin()+j);
            }
        }
        return lists[0];
    }
};

注意,要判定一开始给定的vector是否为空。并且,合并了两个链表以后要把后一个在vector中删除。
接下来就是如何合并两个链表了。根据书上的伪代码,可以运用递归,每次都选出两个链表头最小的元素,接着把剩下的元素再按同样的方法筛选。具体代码如下:

ListNode* mergeTwoLists(ListNode* &firstList, ListNode* &secondList) {
    if (firstList == NULL) {
        return secondList;
    }
    if (secondList == NULL) {
        return firstList;
    }
    if (firstList->val <= secondList->val) {
        firstList->next = mergeTwoLists(firstList->next, secondList);
        return firstList;  
    }
    else {
        secondList->next = mergeTwoLists(firstList, secondList->next);
        return secondList;
    }
};

这样,两两合并的复杂度是O(N),将K条链合成一条的复杂度是O(logK),最终的复杂度只有O(NlogK)。


当然,合并的时候也可以不用递归,也就是暴力比较。这样做的复杂度是一样的,也是O(NlogK)。因此第二种做法如下:

void mergeTwoLists(ListNode* &firstList, ListNode* &secondList) {
    if (firstList == NULL) {
        firstList = secondList;
        return ;
    }
    if (secondList == NULL) {
        return ;
    }
    ListNode *pointer1 = firstList, *pointer2 = secondList, *result = NULL;
    int bigger = 0;
    if (firstList->val > secondList->val) {
        bigger = 1;
        result = secondList;
    }
    else {
        bigger = 2;
        result = firstList;
    }
    while (true) {
        if (bigger == 2) {
            while (pointer1->next) {
                if (pointer1->next->val <= pointer2->val) {
                    pointer1 = pointer1->next;
                }
                else {
                    break;
                }
            }

            if (pointer1->next) {
                ListNode *temp = pointer1->next;
                pointer1->next = pointer2;
                pointer1 = temp;
                bigger = 1;
            }
            else {
                pointer1->next = pointer2;
                firstList =  result;
                return ;
            }
        }
        else {
            while (pointer2->next) {
                if (pointer2->next->val <= pointer1->val) {
                    pointer2 = pointer2->next;
                }
                else {
                    break;
                }
            }
            if (pointer2->next) {
                ListNode *temp = pointer2->next;
                pointer2->next = pointer1;
                pointer2 = temp;
                bigger = 2;
            }
            else {
                pointer2->next = pointer1;
                firstList =  result;
                return ;
            }
        }
    }
};

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.empty()) {
            return NULL;
        }
        while (lists.size() > 1) {
            for (int i = 0, j = 1; i < lists.size() && j < lists.size(); i++, j++) {
                mergeTwoLists(lists[i], lists[j]);
                lists.erase(lists.begin()+j);
            }
        }
        return lists[0];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值