23、合并K个排序链表

问题描述

23、合并K个排序链表_第1张图片

问题分析

一看到这种合并K个数组/链表的题目,立即就要想到分治法:

合并K个链表,可理解为先将其划分为两组 K 2 \frac{K}{2} 2K个链表分别进行合并,两个 K 2 \frac{K}{2} 2K个链表又可以划分为四个 K 4 \frac{K}{4} 4K个链表……直到划分为每组中只有一个链表,然后在将各组合并结果依次层层合并,即可得到最终合并结果

合并图示如下(转载于官方题解):
23、合并K个排序链表_第2张图片


解法:分治法

  • 时间复杂度:O( N log ⁡ k N\log{k} Nlogk ),其中 N N N表示所有链表的节点总数, k k k表示链表数。因为分治的特点,划分的次数为 log ⁡ 2 k \log_2{k} log2k次。另外,合并两个链表的时间主要花费在比较上,若两个链表的总节点数为 n n n,那么一次合并所需的时间为O( n n n )。综合来看,总的花费时间为O( N log ⁡ k N\log{k} Nlogk )。
  • 空间复杂度:O( 1 1 1 )。空间消耗主要在于合并两个链表时,由于是只申请了常量级的空间,并且链表间就地合并,所以只花费了O( 1 1 1 )的空间。

Java代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0){
            return null;
        }

        //定义长度与步长
        int len = lists.length;
        int step = 1;

        //按照不同步长扫描一遍
        while (step < len){
            //依次合并
            for (int i = 0; i + step < len; i = i + step * 2) {
                lists[i] = merge(lists[i], lists[i+step]);
            }
            //步长更新
            step *= 2;
        }

        return lists[0];
    }
    
    static ListNode merge(ListNode a, ListNode b){
        //指针
        ListNode curA = a;
        ListNode curB = b;

        ListNode result = new ListNode(0);
        ListNode cur = result;

        while (curA != null && curB != null){
            cur.next = curA.val < curB.val ? curA : curB;
            cur = cur.next;

            if (curA.val < curB.val){
                curA = curA.next;
            }else {
                curB = curB.next;
            }
        }

        if (curA != null){
            cur.next = curA;
        }else {
            cur.next = curB;
        }

        return result.next;
    }
}

结果分析

以上代码的执行结果:

执行时间 内存消耗
7 ms 42.8MB

23、合并K个排序链表_第3张图片

你可能感兴趣的:(LeetCode)