Leetcode 每日一题:Merge k Sorted Lists

写在前面:

今天我们来看的题目是 Merge K sorted Lists。看名字好像是一道很简单的题,但其实蕴藏的一些优秀解法值得我们一起讨论和学习。

注意,这道题目的 lists 都是烦人的 Linked List,意味着虽然这些 list 都是 sorted 的,但是他们不能像 Merge K sorted Array 一样利用 动态数组 的方便特性轻易取得结果,那我们可以怎么思考呢?

题目介绍:

题目信息:

题目问题:

  • 给定 K 个有序链表,将他们按序合并成一个单一链表
  • k <= 10^4
  • 链表长度小于 500
  • 例子:
    • [1-> 2 -> 3]
    • [1-> 4 -> 5]
    • [2-> 6]
    • 需要被合并成 [1->1->2->2->3->4->5->6] 顺序无所谓

题目想法 MindSet:

从合并双链表开始:

  • 参考类似的合并有序数组的想法,既然链表也是有序的,那对于两个链表来说,只需要使用两个指针进行比较,取小的那一个填入,然后将被填入的指针往前移动,再进行比较,直到其中一个 list 被使用完,合并结束
  • 对于两个链表的合并可以参考 https://leetcode.com/problems/merge-two-sorted-lists/
  • 我们可以举一反三,遍历 k 个链表对每组相邻的链表都做类似合并
  • Runtime O(KN),K 是所有的链表总数
  • 缺点:是一个可行的解决方案,但是会随着 K 的变大而有些缓慢
  • 优点:不需要额外的存储空间,Space:O(1)

能否更好的利用“有序”的关系:

  • 既然合并两个链表可以依靠双指针的想法,那对于 k 个链表,可不可以考虑 k 指针的方法?
  • 即,在每一步,都把当前 k 个数组指针上的数字进行比较,take 最小的那个,然后将那一个指针向前移动,直到所有合并结束?
  • 在每一次比较的时候我们需要遍历 K 个指针上的大小来决定极值
  • Runtime 依旧是 O(KN)

在比较时做的更好?

  • 我们目前对于每一个 step 上的比较是线性的,有没有一种方法可以将比较做的更好?
  • Priority_Queue / Min-heap !!!!!

题目解法:

  • 将所有 链表 的第一个元素存入 Minheap 中
  • 循环直到 Minheap 中不再有任何元素:
    • 将 Minheap 中最小元素代表的 Node 填入结果序列中
    • Minheap 中删除掉这个已使用最小元素
    • 如果 Node -> next 不为 nullptr,填入 Minheap 中
  • Tip: 在只有 Maxheap 的语言中(如 C++),如果要使用 minheap,可以将所有元素值 * (-1)

题目代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    // set as min-heap based on val
    priority_queue<pair<int, ListNode*>> compare;
    
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* res = new ListNode(0);  //dummy head
        ListNode* begin = res;
        
        //push every first point into the min heap since it is sorted:
        for(int i = 0; i < lists.size(); i++){
            if(lists[i] != nullptr){
                compare.push({lists[i]->val * (-1), lists[i]}); //trick to do the minheap
            }
        }
        
        //for every iteration, get the top priority for the next and push it next to the list:
        while(!compare.empty()){
            //let the current min to be append to the list, and proceed the pointer
            ListNode* curr_min = compare.top().second;
            res->next = curr_min;
            res = res->next;
            
            //push the next one from its original list to the heap
            compare.pop();
            if(curr_min->next){
                compare.push({curr_min->next->val * (-1), curr_min->next});
            }
        }
        //one step forward cuz we set up a dummy start
        return begin->next;
    }
};
  • Runtime:O(N*logK)
  • Space: O(K) - for Minheap storage
  • 我们用更大的空间优化了他的执行时间

写在后面:

其实我使用的方法并不是最优的解法(时间 + 空间),但我认为这是最好理解且也最具代表性的一种思路。他同时包括了 Minheap 的特性使用与合并有序链表的思维,比较有代表性,也能通过一道题为大家带来更多收获。

如果使用 Divide and Conquer 方法对于 one by one 合并进行优化,可以在 Runtime 做到与我的方法相同的情况下,Space 做到 O(1),因为他不需要使用额外的存储空间,感兴趣的同学可以去自行阅读解法,我认为是过于难了不如我的方法解决易懂😁,大家在 OA 面试的时候也不一定就能想的出来那么难的,反正我做不到,大牛轻喷~~

Leetcode 答案地址:https://leetcode.com/problems/merge-k-sorted-lists/editorial/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值