给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
提示:
进阶:你可以设计一个只用 O ( 1 ) O(1) O(1) 额外内存空间的算法解决此问题吗?
此题目是[leetcode] 24. 两两交换链表中的节点的进阶版,没做过这道题的童鞋可以先看这道。
既然题目提示只用** O ( 1 ) O(1) O(1)** 额外内存空间,那就不能存储排序结点。具体要怎么实现呢?我简单说一下思路。
第一,我们要做的是每遍历 k k k个结点,就要将 k k k个结点翻转,这只需要三个指针就可以做到。一个是 f r o n t front front指针;一个是 a f t e r after after指针,代表原链表 f r o n t front front后面的结点;一个是 n e x t A f t e r nextAfter nextAfter指针,代表原链表 a f t e r after after后面的结点。为了实现翻转节点,我们只需要将 a f t e r . n e x t after.next after.next指向 f r o n t front front,再将 f r o n t front front指向 a f t e r after after, a f t e r after after指向 n e x t A f t e r nextAfter nextAfter,重复上面的步骤直到翻转k个结点。
第二,我们记结果链表的尾结点为 t a i l tail tail。我们需要做的是将 t a i l tail tail指向翻转 k k k个结点后的头结点,其实就是遍历完成后的 f r o n t front front指针。然后再用 t m p T a i l tmpTail tmpTail指针记录翻转 k k k个结点后的尾结点, t a i l . n e x t tail.next tail.next指向 f r o n t front front之后,再将 t a i l tail tail指向 t m p T a i l tmpTail tmpTail。
第三,遍历到最后时会出现剩余结点不到 k k k个结点的情况,此时就不需要翻转剩余结点。那我们怎么判断后面有多少结点呢?我们可以设置一个 f a s t fast fast指针,初始时 f a s t fast fast就往后移动 k k k个结点。在下一次遍历k个结点之前,先判断 f a s t fast fast指针是否为空。如果为空,则代表剩余结点不到 k k k个,此时只需要将 t a i l . n e x t tail.next tail.next指向 a f t e r after after(翻转完 k k k个结点后, a f t e r after after刚好指向剩余结点的头结点);若不为空,说明剩余结点不少于 k k k个,则需要按步骤一进行后续结点翻转遍历。
ListNode结构
public class ListNode {
public int val;
public ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
翻转链表方法
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || head.next == null || k == 1) {
return head;
}
//fast结点每次都比上k个结点翻转后的头结点多往后遍历k个结点
ListNode fast = head;
for (int i = 0; i < k - 1; i++) {
if (fast != null) {
fast = fast.next;
}
}
//说明链表没有k个结点,直接返回头结点
if (fast == null) {
return head;
}
ListNode dummyNode = new ListNode(0, null);
ListNode tail = dummyNode;
//每次翻转k个结点后的头结点
ListNode front = head;
ListNode after = head.next;
while (true) {
//记录每次翻转k个结点后的尾结点
ListNode tmpTail = null;
for (int i = 0; i < k - 1; i++) {
if (i == 0) {
//翻转k个结点后的尾结点
tmpTail = front;
}
if (fast != null) {
fast = fast.next;
}
//after.next指向front,after和front各往后移动一个结点
ListNode nextAfter = after.next;
after.next = front;
front = after;
after = nextAfter;
}
//当前结果链表的尾结点的next指针指向k个结点翻转后的头结点
tail.next = front;
if (fast != null) {
fast = fast.next;
}
if (fast == null) {
//剩余未遍历的结点不到k个,当前k个结点翻转后的尾结点的next指针指向剩余结点的头结点
tmpTail.next = after;
break;
}
//下次翻转k个结点之前,先将结果链表的尾结点指向当前翻转k个结点后的尾结点
tail = tmpTail;
front = after;
after = after.next;
}
return dummyNode.next;
}
时间复杂度: O ( N ) O(N) O(N), N N N为链表长度,需要遍历链表一次。
空间复杂度: O ( 1 ) O(1) O(1),只有常数级别的指针数量需要存储。
[leetcode] 24. 两两交换链表中的节点