25. K 个一组翻转链表 - 力扣(LeetCode)
一、题目要求
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例 1:
输入:head = [1,2,3,4,5], k = 2 输出:[2,1,4,3,5]
示例 2:
输入:head = [1,2,3,4,5], k = 3 输出:[3,2,1,4,5]
提示:
- 链表中的节点数目为
n
1 <= k <= n <= 5000
0 <= Node.val <= 1000
进阶:你可以设计一个只用 O(1)
额外内存空间的算法解决此问题吗?
二、解法1-双层递归 O(N) 进阶
这个题与翻转链表(每日一练:反转链表-CSDN博客)类似,但是它是分成了几个组分别进行反转,反转链表时我们使用递归来完成,这个题我们很容易想到把链表分层几部分分别递归,但是难点在于各个部分反转后还需要进行连接,这就又需要一层递归,即:
先递归(外层递归)到最后一个要进行反转的部分,进行递归反转(内层递归)后,得到它的新头后返回上一层递归(外层递归);上一层递归是前一个要进行反转的部分,这部分又进行递归反转后将新尾连接到上一次返回的新头即可
新头就是旧尾,要返回给调用它的上一次函数,也就是链表的前一部分。
新尾就是旧头,我们可以在外层递归时保存这个节点,得到后一部分的新头后指向它。
外层递归是为了以从后向前的顺序枚举到所有需要反转的组;
内存递归就是为了翻转这些组;
class Solution {
ListNode* __reverseKGroup(ListNode* last, ListNode* cur, int k) { // 内层递归
if (k == K)
{
cur->next = last;
return cur;
}
ListNode* newhead = __reverseKGroup(cur, cur->next, k+1);
cur->next = last;
return newhead; // 返回新头
}
ListNode* _reverseKGroup(ListNode* cur, ListNode* head, int k) { // 外层递归
if (cur == nullptr || cur->next==nullptr && k != K)
{
return head;
}
if (k == K)
{
ListNode* head_next = _reverseKGroup(cur->next, cur->next, 1); // 得到下一部分的新头以连接它
ListNode* newhead = __reverseKGroup(head_next , head, 1); // 得到这部分的新头,并连接下部分的新头
return newhead; // 返回这部分的新头给前一部分
}
return _reverseKGroup(cur->next, head, k+1);
}
public:
ListNode* reverseKGroup(ListNode* head, int k) {
K = k;
return _reverseKGroup(head, head,1);
}
private:
int K;
};