这是我参与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个大链表。
其实这里还有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;
}
}
}