LeetCode 23. 合并K个升序链表

172 阅读2分钟

一、题目

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

二、思路

首先,合并k个升序链表或许有难度,我们可以先试着合并2个升序链表。

/**
 * 对于两个按照升序排列的链表,将它们合并到一个升序链表中,返回合并后的链表
 */
public ListNode mergeTwoLists(ListNode left,ListNode right) {
    //合并后的新链表,采用两个指针指示,我们用headPointer.next指向链表的头节点,tailPointer指向链表的尾节点
    ListNode headPointer=new ListNode();//空节点
    ListNode tailPointer=headPointer;
    //当left和right都不为空的时候,将较小的节点放入新链表
    while(left!=null && right!=null) {
        if(left.val<right.val) {
            tailPointer.next=left;
            tailPointer=left;
            //将left的指针后移
            left=left.next;
        }else {
            tailPointer.next=right;
            tailPointer=right;
            //将right的指针后移
            right=right.next;
        }
    }
    //如果left和right其中有一个为null,那就直接将tailPointer.next指向另一个ListNode就可以了。
    tailPointer.next=left==null?right:left;
    return headPointer.next;
}

然后,我们再思考合并k个升序链表的事情,只要顺序合并即可,代码如下:

public ListNode mergeKLists(ListNode[] lists) {
    ListNode ans = null;
    for (int i = 0; i < lists.length; i++) {
        ans = mergeTwoLists(ans, lists[i]);
    }
    return ans;
}

但是,很明显这个算法的时间复杂度非常高。
假设有k个链表,每个链表的长度为n,第一次合并之后 ans 的长度为n,第二次长度为2n,第 i 次长度为 i*n,第 i 次的时间复杂度为O(n+(i-1)*n)=O(i*n),总的时间复杂度为O(k²n)

另外,我们想到与这个问题比较类似的,就是归并排序,可以采用分治的思想:将原问题分解为几个规模较小但类似原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

具体到这个问题,操作如下:

  • 分解: 将长度为length的链表数组lists,分解为两个长度为length/2的链表数组list1和list2。
  • 解决: 使用递归方法,将链表数组list1 都合并到 left链表中,list2 都合并到 right链表中。(当链表数组的长度为1时,递归开始回升,不需要做任何操作,因为这个链表本身已经按照升序排列)
  • 合并: 将left链表和right链表合并。
/**
 * 将链表数组lists的第p项到第r项链表合并到一个链表
 */
public ListNode merge(ListNode[] lists,int p,int r) {
    if(p>r) return null;
    //这个链表数组的长度为1的时候,直接返回
    if(p==r) return lists[p];
    //以q为分界线,将数组lists分为lists1和lists2
    int q=(p+r)/2;
    //将lists1合并到left链表
    ListNode left=merge(lists, p, q);
    //将lists2合并到right链表
    ListNode right=merge(lists, q+1, r);
    //将left和right两个链表合并
    return mergeTwoLists(left, right);   
}

public ListNode mergeKListNodes(ListNode[] lists) {
    return merge(lists, 0,lists.length-1);
}

qrcode2.png