LeetCode 23. Merge k Sorted Lists

分治算法练习(二)

这周课上学习的还是分治算法,比较高级的内容是利用快速傅立叶进行的多项式乘法计算。然而网站上divide & conquer的tag似乎没有这类题目(倒是有许多dp的题)(虽然即使有..也不会做…)

嗯..于是避开dp继续选择了归并排序的题目,难度为Hard。(感觉实际难度只能算Easy?)

题目描述很简单:

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.


想要通过这道题真的很容易了,基本思路是将有序的list两两进行归并排序,并将得到的⌈k/2⌉个稍大的list继续进行两两归并,直到得到最终的一个list。

时间复杂度为O(nlogn).


第一次尝试

一开始为了省事(自以为省事),直接使用了一个queue,将所有表头的ListNode*入队,每次merge队头两个list,将其弹出,并将结果放入队尾,直到queue中只剩下一个ListNode*。

然而通过后得到的结果是:
130 / 130 test cases passed.
Status: Accepted
Runtime: 33 ms
Your runtime beats 48.05 % of cpp submissions.

竟然还未达到中等水平,这个方法可能是不能接受了。

第二次尝试

经过思考认为可能是队列操作严重影响了效率,于是决定放弃使用队列。
考虑每次归并都将结果的表头ListNode*储存在被合并的两个表中的前一个表头所在的位置,即lists[a] = mergeTwoLists(lists[a], lists[b]); 而每次的都将在lists中相邻的两个表进行归并。
把对lists进行的每一次遍历所进行的归并称作一趟,则第k趟中相邻两表是lists[a]和lists[a + 2^(k-1)]。
显然,在2^(k-1) >= lists.size()时归并排序完成。
这种做法有效利用了原本的存储空间,减少了空间的浪费和时间的开销。

具体代码如下:

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.size() == 0) return NULL;
        int k = 1;
        ListNode* a, *b;
        while (k < lists.size()) {
            for (int i = 0; i < lists.size(); i = i+k) {
                a = lists[i];
                if (i+k < lists.size()) {
                    i += k;
                    b = lists[i];
                    lists[i-k] = mergeTwoList(a, b);
                }
            }
            k = k*2;
        }
        return lists[0];
    }

    ListNode* mergeTwoList(ListNode* a, ListNode* b) {
        if (a == NULL) return b;
        if (b == NULL) return a;
        ListNode* p, *head;
        ListNode* pa = a,
                  *pb = b;
        if (a->val < b->val) {
            p = a;
            pa = pa->next;
        } else {
            p = b;
            pb = pb->next;
        }
        head = p;
        while (pa != NULL && pb != NULL) {
            if (pa->val < pb->val) {
                p->next = pa;
                pa = pa->next;
                p = p->next;
            } else {
                p->next = pb;
                pb = pb->next;
                p = p->next;
            }
        }
        while (pa != NULL) {
            p->next = pa;
            pa = pa->next;
            p = p->next;
        }
        while (pb != NULL) {
            p->next = pb;
            pb = pb->next;
            p = p->next;
        }
        return head;
    }
};

通过后得到的结果是:
130 / 130 test cases passed.
Status: Accepted
Runtime: 23 ms
Your runtime beats 99.48 % of cpp submissions.
明显优于第一次的提交。

可以说这次的是一道比较容易的题目,但在具体实现上稍微需要费些心思。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值