leetcode-合并K个升序链表

414 阅读3分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

今天也是一个难得早起的周六,外面下雨哪儿也不想去,做饭看书看电视,周末就该这么慵懒的度过吧。
上午看了一会儿美团的技术博客,上面的文章还挺好的,要么是满满干货的纯技术,要么是深度的思考,基本都是精品。见贤思齐,差距还很大,不过每天写一篇练练手,应该也会逐渐可以写出好文章。

今天做的是leetcode的第23题,标定的难度是困难,不过其实有前几天的1题差不多的做铺垫,也还好。

题目

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:
输入: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

示例 2:
输入:lists = []
输出:[]

示例 3:
输入:lists = [[]]
输出:[]

思路

前几天有1题是合并2个升序链表,今天的题目是合并K个。其实有了合并2个链表的方法后,问题就简化多了。(合并2个升序链表的题解见《leetcode-合并两个有序链表》)。可以采取分治的思想,想把1个大问题分成2个小问题,然后合并2个小问题的解,这种思想可以不断递归,子问题可以分成更小的子问题,只要有合并子问题解的能力,就可以一直分下去,直到变成1个已经存在解的问题。具体到这道题目上,可以从下到上,先22合并,到上面1层,再对合并的结果再22合并,直到合并成1个大链表。

分治.png

其实这里还有1个朴素的算法思想,就是把未知解的问题转化成已知解的问题,然后直接复用已知解法就行。这个思想还有一个段子:问题1是烧一壶开水,已知解是先拿1个空的烧水壶,装满水,放到燃气灶,打开燃气,水开后再关闭燃气灶;求解问题2,如果现在烧水壶中已经装满水了,怎么烧一壶开水;有人给出的解是,先把水倒掉,这样就转换成问题1了。初看这个段子会哈哈一笑,仔细一想,虽然人看起来这个解有点傻,但其实已经是仅次于最优解的一个解法了,甚至某些情况下,就是最优解。它的优点是,直接把问题1的解看成了1个黑盒,我们不用关心问题1是怎么解,只要明确问题1有已知解,我们求解问题2的时候,只花了1步,倒掉水壶中的水,就把问题2转化成了已知解的问题1。如何把1个问题快速转换成为一个已知解的问题,也是程序猿必须具备的能力。

回到这题,我们采用分治,不停的去分这个链表的数组,最终就分到了合并2个链表的已经解的问题,这样就可以直接使用前2天的已知解,站在前人的肩膀上。

Java版本代码

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

    private static ListNode mergeKLists(ListNode[] lists, int startIndex, int endIndex) {
        if (startIndex > endIndex) {
            return null;
        }
        if (startIndex == endIndex) {
            return lists[startIndex];
        }
        int mid = (startIndex + endIndex) / 2;
        return mergeTwoLists(mergeKLists(lists, startIndex, mid), mergeKLists(lists, mid + 1, endIndex));
    }

    public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        } else if (l2 == null) {
            return l1;
        }
        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}