题目描述
给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序。
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明:
- 你的算法只能使用常数的额外空间。
- 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
算法
边界条件
-
k值与链表长度。虽然题目说k 是一个正整数,它的值小于或等于链表的长度。但是实际提交的时候我发现。。。没错,k值大于链表长度也被作为一个测试点。
-
链表为空或者k值为1
思路
- 找到两个距离为k的节点
- 反转两个节点之间的这段链表
- 重复1、2步直到遍历完整个链表
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* global_head;
ListNode* reverseKGroup(ListNode* head, int k) {
// 边界条件
if(head == NULL || k == 1)
return head;
int size = 0;
for(ListNode* t = head; t; t = t->next, size++);
if(size < k)
return head;
/*** H靠近链表头部,tail靠近链表尾部;
pre_node定义为前一段已经反转的局部链表的最后一个节点;
比如链表1->2->3->4->5->6,k=2;第一段局部链表反转为2->1,那么pre_node指向1这个节点;
pre_node初值设为NULL,可以在reverse_k中判断是不是第一段局部链表的反转,如果是第一段的话,需要设置反转后的链表头部节点global_head
***/
ListNode *H, *tail, *pre_node = NULL;
H = tail = head;
// 遍历整个链表
while(true)
{
// cnt用来记录H和tail节点之间的距离,
int cnt = 0;
while(cnt < k && tail != NULL)
{
tail = tail->next;
cnt++;
}
// 1->2->3,k=3
if(tail == NULL && cnt == k)
{
reverse_k(pre_node, H, tail);
break;
}
// 最后剩余节点不足k个
else if(cnt != k)
{
pre_node->next = H;
break;
}
// 对k个节点反转
else
{
reverse_k(pre_node, H, tail);
H = tail;
}
}
return global_head;
}
void reverse_k(ListNode *&pre_node, ListNode *&start, ListNode *&end) {
/*** 这个函数将[start, end)之间的节点顺序逆置 ***/
ListNode *pre, *p, *post;
pre = start;
p = pre->next;
// 反转代码
while(p != end)
{
post = p->next;
p->next = pre;
pre = p;
p = post;
}
// 判断是不是第一段局部链表的反转以设置改动后的链表头节点global_head
if(pre_node == NULL)
global_head = pre;
else
// pre_node是前一段链表的最后一个节点,接上这段链表
pre_node->next = pre;
// 重新设置pre_node为这段链表的最后一个结点
pre_node = start;
pre_node->next = NULL;
}
};