方法一:
思路与算法
根据题干,很容易可以联想到,使用递归地方式,只需要翻转链表的前 K 个元素,再将第 K 个元素的 next 指针,指向链表剩余节点反转后的结果即可。
既然使用递归,需要明确的是,递归的两个条件: 递归条件:链表的长度不小于 K 时,递归继续; 边界条件:链表的长度小于 K 时,递归结束,剩余节点不再翻转,直接拼到链表末尾。
值得说明的是,这里可以使用额外的方法来实时计算链表长度,以判断是否满足递归条件,但是这样无疑多进行了一次链表遍历,增加了算法的复杂度。比较巧妙的方法是,在移动指针指向第 k 个节点的过程中,如果当前节点已经为空了,说明链表的长度必定是小于 k 的,此时只需要直接返回头节点 head 即可。
翻转链表在 leetcode 中原题 206,考虑到题目要求只使用常量级别的额外空间,所以这里使用双指针法,实现翻转链表。
完整代码如下:
public class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null) return head;
ListNode kthNode = head;
for (int i = 0; i < k - 1; i++) {
kthNode = kthNode.next;
if (kthNode == null) return head;
}
ListNode nextNode = kthNode.next;
kthNode.next = null;
ListNode reversedHead = reverse(head);
ListNode reversedTail = reversedHead;
while (reversedTail.next != null) {
reversedTail = reversedTail.next;
}
reversedTail.next = reverseKGroup(nextNode, k);
return reversedHead;
}
public ListNode reverse(ListNode head) {
ListNode dummyNode = new ListNode(0);
ListNode curr = head;
while (curr != null) {
ListNode currNext = curr.next;
ListNode dummyNext = dummyNode.next;
dummyNode.next = curr;
curr.next = dummyNext;
curr = currNext;
}
return dummyNode.next;
}
}
复杂度分析
时间复杂度:O(n),其中 n 为链表的长度。要进行反转的次数为(n/k),每次反转的复杂度为O(k)。 空间复杂度:O(1),我们只需要建立常数个变量。
总结
本题的难度为困难,但总体来说并没有涉及到什么复杂算法,主要的难点在于:
- 常数级别的空间复杂度返回链表;
- 递归反转时,把握递归条件与边界条件。
|