leetcode 25,Reverse Nodes in k-Group,难度 hard
0. 题干
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明:
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
1. 代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* dummy=new ListNode(0);
dummy->next=head;//dummy->next存储链表头指针
ListNode* node_first=head;// 存储链表首元地址,即结点1地址
ListNode* pre=dummy;
ListNode* cur=head;
ListNode* tail=dummy;
while(true){
int count=0;
tail=pre;
while(tail!=NULL&&count<k){
tail=tail->next; //退出循环后tail指向待反转链表的末尾节点
count++;
}
if(tail==NULL)
break;
while(pre->next!=tail){ //pre->next==tail时退出循环
cur=pre->next;//(1)
pre->next=cur->next; //(1) 步骤(1):将cur从链表中切出来
cur->next=tail->next;//(2)
tail->next=cur; //(2) 步骤(2):将cur添加到tail后
}
pre=node_first;
tail=node_first;
node_first=pre->next;//head指向新的待翻转的链表头
}
return dummy->next;
}
};
2. 代码流程理解
假设原链表有7个结点,K的值为3,接下来一句一句跑下代码;由于链表有指向的概念(其实就是指针,以下为了区分,统一把链表的next指针表述为指向,各个结点的指针表述为存储地址)
初始化空链表dummy,dummy指向头指针head;
node_first存储首元结点的地址;
pre存储dummy的地址;
cur存储head的地址;
tail存储dummy的地址;
2.1 第一重外面while(true)循环
进入while循环,count初始化为0,tail和pre相等,此时都为dummy的地址;
tail当然不为NULL,0<3 ;
tail递增存储结点1的地址;
count递增为1;
count=1<3,然后再次进入while循环,
tail递增存储结点2的地址;
count递增为2;
count=2<3,然后再次进入while循环,
tail递增存储结点3 的地址;
count递增为3
不满足循环继续条件,所以while循环跳出;
if ( tail == NULL) 然后break这句,是while(true)的循环退出条件,总不能整个死循环吧…
while(pre->next!=tail){,前面已知,pre指向结点1,而此时tail存储结点3的
地址,所以再次进入while循环,
cur存储结点1的位置;
pre指向结点2;
结点1指向结点4
结点3指向结点1;
这波运行完之后,链表关系为:
2 -> 3 -> 1 ->4 ->5 ->6 ->7->NULL
由于pre指向结点2,不为结点3,所以while循环继续;
cur存储结点2的位置;
pre指向结点3;
结点2指向结点1;
结点3指向结点2;
这波运行完之后,链表关系为:
3->2->1->4->5->6->7->NULL
接下来pre存储链表头的位置,即结点1的位置,
tail存储结点头的位置,即结点1的位置,
node_first存储结点4的位置;
2.2 第二重while(true)循环
count 初始化为0;
tail和pre都存储结点1的位置;
while(tail!=NULL&&count<k){ 这个运行完之后,tail存储结点6的位置;
pre指向结点4,不指向结点6,所以进入 while(pre->next!=tail) 循环,
cur存储存储结点4的位置;
结点1指向结点5;
结点4指向结点结点7
结点6指向结点4
这波运行完之后,链表关系为:
3->2->1->5->6->4->7->NULL
此时pre指向结点5,而不是结点6,所以while循环继续,
cur存储结点5的位置;
pre指向结点6,即结点1指向结点6;
结点5指向结点结点4;
结点6指向结点5;
这波运行完之后,链表关系为:
3->2->1->6->5->4->7->NULL
跳出 while(pre->next!=tail) 循环,
然后pre存储结点4的位置;
tail存储结点4的位置;
node_first存储结点7的位置;
2.3 第三重while(true)循环
count初始化为0;
tail和pre都存储结点4的位置
while(tail!=NULL&&count<k){
由于k为3,运行一次,tail存储结点7的位置,
再运行一次,tail存储NULL,所以while循环跳出,
然后进入if ( tail == NULL) 跳出整个while循环;
3. 补充说明
这里特别补充,链表的头指针,头结点和首元结点;
头指针就是一个指针,没有数据,指向整个链表;如果没有头节点,那么头指针就是首元结点的地址。
一般来说,leetcode里面的链表是没有头结点的;
链表是带头结点的,头指针会始终指向头结点,不会改变。
如果链表不带头节点,头指针会始终指向队首元素,会改变。
这就是下面这句代码的意义。
return dummy->next;
更具体可参考:https://blog.csdn.net/u012531536/article/details/80170893