1. 题目描述
在C++中,给定一个双向链表和一个整数k,把链表按照k的大小分组,并将每组内部的节点反转。如果剩余节点不足k个,则将剩余节点反转。
例如,给定链表: 1->2->3->4->5,k=2,则应将链表反转为2->1->4->3->5。给定链表: 1->2->3->4->5,k=3,则应将链表反转为3->2->1->4->5。
2. 解题思路
该题可以分成两部分:将链表按照k的大小分组,每个小组内部反转。
首先,我们需要遍历整个链表,计算出链表的长度。接下来,我们以k为步长,依次对链表进行分组。每个分组长度为k,如果当前分组长度不足k,则将剩余节点全部放在最后一个分组中。
对于每个分组,我们都要将其内部进行反转。具体操作是:取出当前分组的首节点作为当前分组的尾节点,并用一个指针p记住该节点;然后将当前分组的首节点后面的一个节点作为当前分组的首节点,并重复上述操作,直到将整个分组反转完成。最后,将上一个分组的尾部和当前分组的首部相连即可。
3. 代码实现
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode* prev;
ListNode* next;
ListNode(int x) : val(x), prev(NULL), next(NULL) {}
};
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if (!head || !head->next || k <= 1) {
return head;
}
// 遍历计算链表长度
int len = 0;
ListNode* p = head;
while (p) {
len++;
p = p->next;
}
// 分组并反转
ListNode* dummy = new ListNode(-1);
dummy->next = head;
ListNode* pre = dummy;
while (len >= k) {
ListNode* cur = pre->next;
ListNode* tail = cur;
// 分组
for (int i = 1; i < k; i++) {
if (!tail) {
// 不足k个,不用反转
return dummy->next;
}
tail = tail->next;
}
ListNode* next = tail->next;
// 反转
while (cur != next) {
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
// 连接每个分组
ListNode* tmp = pre->next;
pre->next = tail;
pre = tmp;
pre->prev = tail;
len -= k;
}
return dummy->next;
}
};
int main() {
ListNode* a = new ListNode(1);
ListNode* b = new ListNode(2);
ListNode* c = new ListNode(3);
ListNode* d = new ListNode(4);
ListNode* e = new ListNode(5);
a->next = b;
b->prev = a;
b->next = c;
c->prev = b;
c->next = d;
d->prev = c;
d->next = e;
e->prev = d;
Solution s;
ListNode* res = s.reverseKGroup(a, 2);
while (res) {
cout << res->val << " ";
res = res->next;
}
return 0;
}
3.1 代码说明
在代码中,我们使用了一个哨兵节点dummy,它的next指针指向了链表的头节点head。使用dummy节点的好处是,在处理链表中的边界问题时,我们无需单独考虑头节点和尾节点的情况。
该算法中用到了4个指针,它们的含义如下:
- pre:当前待反转的分组的前一个节点;
- cur:当前待反转的分组的首节点;
- tail:当前待反转的分组的尾节点;
- next:当前待反转的分组的下一个节点。
其中,pre的初始值为dummy,tail的初始值为cur,next的初始值为tail->next。
具体的算法实现过程见代码注释。
4. 总结
本文分析了将双向链表按给定大小分组反转的方法,按照题目要求,我们做了详细的解题思路说明和代码实现,并使用代码测试了算法的正确性。
在本题中,我们需要注意一些细节问题,例如链表长度不足k时,不需要进行反转操作;在进行分组反转时,需要注意首尾节点之间的连接关系等等。通过本题的练习,我们可以更深入地理解链表的基本操作和指针的使用技巧,对提高我们的编程能力有很大的帮助。