题目来源
题目描述
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
}
};
题目解析
分析
- 每k个一组来翻转链表,实际上是把原链表分成若干小端,然后分别对其进行翻转。
- 那么肯定需要两步:一个分段、一个翻转
- 怎么分段呢?
- 把链表节点按照k个一组分组,所以可以使用一个指针head依次执行每组的头结点。
- 这个指针每次向前移动k步,直到链表结尾
- 对于每个分组,我们先判断它的长度是否>=k
- 如果是,反转这部分链表
- 如果不是,不需要反转
- 如何反转?
- 和一般的反转链表不同:对于一个子链表,除了反转本身外,还需要将子链表的头部和上一个子链表连接,以及子链表的尾部与下一个子链表连接
- 因此,在翻转子链表的时候,我们不仅需要子链表头节点 head,还需要有 head 的上一个节点 pre,以便翻转完后把子链表再接回 pre。
-
但是对于第一个子链表,它的头结点head前面是没有pre的。怎么办呢?
- 方法一:特判
- 方法二:还可以引入一个dummyHead,比如链表 1->2->3->4->5变为 -1->1->2->3->4->5
-
反复移动head和pre,对head所指向的子链表进行反转,直到结尾,就可以答案。
-
问题是:链表翻转之后,链表的头节点发生了变化,那么应该返回哪个节点呢?
- 照理来说,前 k 个节点翻转之后,链表的头节点应该是第 k 个节点。
- 那么要在遍历过程中记录第 k 个节点吗?但是如果链表里面没有 k 个节点,答案又还是原来的头节点。太麻烦了!!!
- 可以注意到,我们之前创建了节点 pre 吗?这个节点一开始被连接到了头节点的前面,而无论之后链表有没有翻转,它的 next 指针都会指向正确的头节点。那么我们只要返回它的下一个节点就好了。
-1->1->2->3->4->5
| | |
pre cur next
-1->3->2->1->4->5
| | |
cur pre next
实现
class Solution {
ListNode* reverseOneGroup(ListNode* pre, ListNode* next) {
// x -- > b -- > c --> d
// pre last curr next
ListNode *last = pre->next, *cur = last->next;
while(cur != next) {
last->next = cur->next;
cur->next = pre->next;
pre->next = cur;
cur = last->next;
}
return last;
}
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if (!head || k == 1) return head;
ListNode *dummy = new ListNode(-1), *pre = dummy, *cur = head;
dummy->next = head;
for (int i = 1; cur; ++i) {
if (i % k == 0) {
pre = reverseOneGroup(pre, cur->next);
cur = pre->next;
} else {
cur = cur->next;
}
}
return dummy->next;
}
};
递归实现
class Solution {
// head: 记录每段的开始位置
// next: 记录结束位置的下一个节点
ListNode* reverseOneGroup(ListNode* head, ListNode* next) {
ListNode *prev = next;
while (head != next){
ListNode *t = head->next;
head->next = prev;
prev = head;
head = t;
}
return prev;
}
public:
// head: 记录每段的开始位置
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode *curr = head;
for (int i = 0; i < k; ++i) {
if(curr == nullptr){
return head;
}
curr = curr->next;
}
ListNode *newHead = reverseOneGroup(head, curr);
head->next = reverseKGroup(curr, k);
return newHead;
}
};
类似题目
题目 | 核心思路 |
---|---|
leetcode:24. 两两交换链表中的节点 Swap Nodes in Pairs | |
leetcode:206. 反转全部链表 | |
leetcode:92. 反转链表区间 [m, n] 的元素 | |
leetcode:143. 重排链表 | |
leetcode:24. 每k个一组反转链表Reverse Nodes in k-Group | |
leetcode:面试题06. 从尾到头打印链表 |