携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
题目描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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 = [[]] 输出:[]
提示:
k == lists.length 0 <= k <= 10^4 0 <= lists[i].length <= 500 -10^4 <= lists[i][j] <= 10^4 lists[i] 按 升序 排列 lists[i].length 的总和不超过 10^4
题目解析
- k个链表都是升序排列的,将它们合并成一个升序链表
- 涉及到链表的合并,可以独立成一个小问题,就是两个链表的合并
解题思路
- 合并两个链表: 需要将当前合并节点之前的节点记录下来,即before;
- 获取当前两个链表节点值,如果链表k1.val > k2.val ,则将k2.val放入链表中,并将before设置成k2,将k2.next和k1继续进行比较,直到k1,k2链表都为空,最终获取到的链表就是合并之后的链表。 多个链表的合并可以拆分成多对链表的合并,这里采用分治法进行合并。
分治法:把一个复杂的问题分成两个或者更多相同或相似的子问题,再把子问题分成更小的子问题,知道最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。 先将数组中的链表分别两两配对合并成新链表,再将这些新链表合并成新链表。。。。。。依次循环直到最后只剩下一个新链表,就是最终要求的合并链表。
代码实现
/**
* @param lists
* @return
*/
public ListNode mergeKLists(ListNode[] lists) {
ListNode res = new ListNode();
if (lists == null || lists.length == 0){
return null;
}
// 多个链表的
return mergeByRange(lists,0,lists.length-1);
}
/**
* 根据下标进行合并
*
* @param lists 需要进行合并的链表数组
* @param first 第一个元素位置
* @param second 第二个元素位置
* @return 返回
*/
private ListNode mergeByRange(ListNode[] lists, int first, int second) {
// 分治调用
// 终止条件
// 如果左右两点相等,直接返回第一个下标元素
if (first == second){
return lists[first];
}
// 如果下标1大于下标2说明已经超过中点,直接返回空(剩余未配对的元素为空)
if (first > second){
return null;
}
// 取中位数
int mid = (first+second)/2;
return merge(mergeByRange(lists,first,mid),mergeByRange(lists,mid+1,second));
}
/**
* 合并两个链表
*
* @param firstListNode 第一个链表
* @param secondListNode 第二个链表
* @return 合并后的新链表
*/
private ListNode merge(ListNode firstListNode, ListNode secondListNode) {
if (firstListNode == null || secondListNode == null) {
return firstListNode == null ? secondListNode : firstListNode;
}
ListNode res = new ListNode(0);
// 这里一定要注意 tai的值一直要指向当前操作节点的上一个节点来进行值的比对
// 当前节点的值是存入next属性中的
ListNode tail = res;
while (firstListNode != null && secondListNode != null) {
if (firstListNode.val > secondListNode.val){
// 第一次遍历时tail指向的对象地址就是res,所以这里设置的next节点实际上就是在给res设置next节点
tail.next = secondListNode;
secondListNode = secondListNode.next;
}else {
tail.next = firstListNode;
firstListNode = firstListNode.next;
}
// 第一次循环之后,将tail的对象地址指向为当前存入的链表地址,即res.next地址,依次循环,一直设置的都是res.next.next.next所以res形成了一个新的链表对象
tail = tail.next;
}
// 最后 判断两个节点是否还有值,放入链表尾部。此时的tail实际上指向的是链表的最后一个节点
tail.next = (firstListNode != null ? firstListNode : secondListNode);
return res.next;
}