携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
题目描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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。将链表按照k个一组进行翻转;
- 翻转是将这个k个看作一个整体,从前向后整个调转,不是两个间前后调转。
- 当剩余的链表个数小于k时,则直接返回这个链表,不进行翻转。
- 要求空间复杂度为 O(1),那么就需要在进行遍历时就将链表进行翻转,而不需要额外采用容器存储。
- 需要注意的是,链表中引用的都是对象,所以修改一个对象会连带着修改它所关联的其它链表节点
代码实现
public ListNode reverseKGroup(ListNode head, int k) {
// 记录翻转后的新链表
ListNode res = new ListNode(0);
res.next = head;
// k组链表的起始节点
// 开始节点的前一个节点
ListNode prev = res;
// 结束节点(不包含next)
ListNode end = res;
while (end.next != null){
// 一次遍历k个 加上外部的while一共遍历n*k次
for (int i = 0; i < k ; i ++){
if (end == null) {
// 如果当前剩余的链表节点数小于k,则直接返回k
return res.next;
}
// 因为end是从虚拟节点开始的,所以它应该是next
end = end.next;
}
if (end == null) {
// 如果当前剩余的链表节点数小于k,则直接返回k
return res.next;
}
// 交换开始的节点 因为pre是从虚拟节点开始的 所以这里的节点需要取next
ListNode start = prev.next;
// 用来记录下一个开始节点
ListNode next = end.next;
// 设置为null后,相当于给翻转提供终止条件,在翻转时如果遇到值为null则停止翻转(这里都是对象引用,所以start节点一直向后调用next时最终会达到end对象)
end.next = null;
// 交换prev和end节点之间的链表
prev.next = reverseByStart(start);
start.next = next;
prev = start;
end = prev;
}
return res.next;
}
/**
* 翻转start链表 终止条件时start.next为null
* @param start
*/
private ListNode reverseByStart(ListNode start) {
// 前一个节点
ListNode pre = null;
// 当前节点
ListNode curr = start;
while (curr != null){
// 前后对象置换
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
- k个链表都是升序排列的,将它们合并成一个升序链表
- 涉及到链表的合并,可以独立成一个小问题,就是两个链表的合并 解题思路