#23 Merge k Sorted Lists (N路归并排序)

#23 Merge k Sorted Lists (N路归并排序)

题目地址:#23

题目分类:链表/归并排序/堆排序

题目难度:hard


题目

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

翻译:合并K个已经排序的链表,返回一个排序好的链表。


思路

  1. 暴力法,我们很容易想到:以一个for循环遍历所有的链表,找出最小的,然后将这个节点加入新的链表
  • 时间复杂度: O(k*n)
  • 点评:简单粗暴,但是性能实在是太弱了。每次都要遍历所有的链表,测试结果TLE
  • 分治法+归并:对于K个链表,利用分治的思想,每次归并2个直到最后剩下一个排序好的链表
    • 时间复杂度: O(n*lgk)
    • 仔细分析一下,实际遍历次数应该恰好是n*lg(k),因为每层归并都会遍历所有元素一次,而一共有lg(k)层
    • 点评:可选的方法,对于链表,不需要引入额外空间。但是考虑数组呢?那就需要大量的额外空间!
  • 最小堆:参考暴力法,对于这K个链表,每次我们做的操作是:

    1 - 找出最小的节点 Node min;

    2 - 将Node min加入新的链表

    3 - Node min = Node min -> next

    在暴力法中,我们的缺陷在于:不能有效地寻找到当前最小的元素。for循环做了许多重复的比较,这无疑是一种浪费

    因此,我们预先将节点数组Node [] 构建成最小堆,然后每次只需要取出第一个元素即可。

    • 时间复杂度: O(k) + O(n*lgk)
    • 关于时间复杂度的说明:这里,构建最小堆花费了O(k)时间,而相比于分治法,显然有:

    O(k) + O(n*lgk) > O(nlgk)

  • 这样想可就错了。。。思路2中提过,分治归并实际上每次是正正好好的:n*lgk次遍历元素次数+n*lgk次比较次数

    相比之下,最小堆除了第一次建队的开销外,其余的开销都在维护堆上。所以这个O(n*lgk)应该是——每个元素最多进行lgk次比较(最坏情况)+每个元素遍历1次

    因此,最小堆在大量数据情况下,绝对是优于分治归并的!

    • 点评:最小堆复杂在建堆+维护堆,但是性能的提升是很大的。
  • 更好的方法——Loser Tree

    待补充


  • 陷阱

    对于难题,反而考察的是算法的实现,没有什么小坑可以跳了。

    这里只需要注意NULL指针的处理即可。

    我选用的方法是,将NULL指针替换为一个value = INT_MAX的节点

    这样这个节点只会被沉到堆底,不需要浪费比较次数

    终止条件设为:首元素为这个INT_MAX节点

    对于这个思路,想到了一些数组方面的排序问题的边界处理。**如果你要在一个数组中寻找某个数字,常规的做法是

    for(int i=0;i!=size;i++){if(a[i]==flag)…}

    但是这样每次都需要比较size和i。可以这样做:边界处放置一个flag,一旦a[i] == flag, 就可以退出循环了。

    以上为题外话


    测试案例

    1. 输入整个为NULL
    2. 输入的数组中含有为NULL的链表
    3. 性能测试,OJ的测试案例包含了10000个链表

    代码

    C

    void MIN_HEAP_SORT(struct ListNode **lists, int index_i,int size)
    {
        int left = index_i*2 + 1;
        int right= index_i*2 + 2;
        if(left>=size)
            return;
        int min;
        if(right>=size)
            min = left;
        else
            min = lists[left]->val<lists[right]->val?left:right;
        if(lists[index_i]->val>lists[min]->val){
            struct ListNode *temp = lists[index_i];
            lists[index_i] = lists[min];
            lists[min] = temp;
            MIN_HEAP_SORT(lists,min,size);
        }
    }
    
    void BuildHeap(struct ListNode **lists,int size)
    {
        for(int i=(size-1)/2;i>=0;--i){
            MIN_HEAP_SORT(lists,i,size);
        }
    }
    
    struct ListNode *mergeKLists(struct ListNode *lists[], int k) {
        if(k==0)
            return NULL;//1
        struct ListNode *head = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode *int_max = (struct ListNode*)malloc(sizeof(struct ListNode));
        int_max->val = INT_MAX;
        int_max->next = NULL;
        struct ListNode *travel = head;
        for(int i=0;i<k;++i){
            if(lists[i]==NULL)
                lists[i] = int_max;
        }/*remove those NULL ptr*/
        BuildHeap(lists,k);
        while(lists[0]!=int_max){
            travel->next = lists[0];
            travel = lists[0];
            lists[0] = lists[0]->next;
            if(lists[0]==NULL)
                lists[0] = int_max;
            MIN_HEAP_SORT(lists,0,k);
        }
        travel->next = NULL;
        return head->next;
    }

    LeetCode上的其他解法

    1. 分治+归并:Divide-and-Conquer
    2. 使用heap类的简洁JAVA版:13lines in java
    3. JAVA优先级队列:A java solution based on Priority Queue
    4. 年度最佳。奢华低调暴力解法:You are a genius…Boy
    时间: 2024-10-31 21:30:09

    #23 Merge k Sorted Lists (N路归并排序)的相关文章

    [LeetCode]23. Merge k Sorted Lists

    23. Merge k Sorted Lists Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 给定k个排序了的链表,合并k个链表成一个排序链表. 本程序思路: 1)首先得到K个链表的长度和存在len中 2)从K个链表中找到值最小的那个节点,把该节点添加到合并链表中 3)重复len次即可把所有节点添加到合并链表中. 注意事项: 1)K个链表中有的

    23. Merge k Sorted Lists - LeetCode

    Question 23.?Merge k Sorted Lists Solution 题目大意:合并链表数组(每个链表中的元素是有序的),要求合并后的链表也是有序的 思路:遍历链表数组,每次取最小节点 Java实现: public ListNode mergeKLists(ListNode[] lists) { ListNode preHead = new ListNode(0); ListNode minNode = getMinNode(lists); ListNode tmpNode =

    23. Merge k Sorted Lists(js)

    23. Merge k Sorted Lists Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. Example: Input: [   1->4->5,   1->3->4,   2->6 ] Output: 1->1->2->3->4->4->5->6题意:按大小顺序合并n条链表代码

    [Leetcode][Python]23: Merge k Sorted Lists

    # -*- coding: utf8 -*-'''__author__ = '[email protected]' 23: Merge k Sorted Listshttps://oj.leetcode.com/problems/merge-k-sorted-lists/ Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. ===Comments by

    leetCode 23. Merge k Sorted Lists (合并k个排序链表) 解题思路和方法

    Merge k Sorted Lists Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 思路:此题是由合并两个排序链表演化而来,刚开始,想法比较简单,像求最大公共前缀一样,逐一求解:但是最后超时,所以马上意识到出题方是为了使用归并和分治的方法,故重新写了代码. 代码一(超时未过): /** * Definition for singly-link

    23. Merge K Sorted Lists (Java, 归并排序的思路)

    题目:Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 解析:合并k个已经有序的单链表,使其最终成为一个有序的单链表.原理就是归并排序,递归运算.基本算法recusion 与 merge 编码: public ListNode mergeKLists(ListNode[] lists) { if(lists == null || lists.leng

    23. Merge k Sorted Lists

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. Subscribe to see which companies asked this question 思路:类似归并排序,每个链表已经排好序了,现在只需要将各个链表合并成一个链表.要点:分而治之,最后合并. /** * Definition for singly-linked list. * st

    leetcode 23. Merge k Sorted Lists(堆||分治法)

    Merge k sorted linked lists and return it as one sorted list. 题意:把k个已经排好序的链表整合到一个链表中,并且这个链表是排了序的. 题解:这是一道经典好题,值得仔细一说. 有两种方法,假设每个链表的平均长度是n,那么这两种方法的时间复杂度都是O(nklogk). 方法一: 基本思路是:把k个链表开头的值排个序,每次取最小的一个值放到答案链表中,这次取完之后更新这个值为它后面的一个值.接着这么取一直到全部取完.那么每次更新之后怎么对当

    【一天一道LeetCode】#23. Merge k Sorted Lists

    一天一道LeetCode系列 (一)题目 Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. (二)解题 合并K个已拍好序的链表.剑指上有合并两个已排好序的链表的算法,那么K个数,我们可以采用归并排序的思想,不过合并函数可能需要修改一下,换成合并两个已排好序的链表的方法.代码如下: /** * Definition for singly-linked