class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
int len=right-left+1;
ListNode* pre_head=nullptr;
ListNode* result=head;
while(head&&--left){
pre_head=head;
head=head->next;
}
ListNode* tail=head;
ListNode* newhead=nullptr;
ListNode* next=nullptr;
while(head&&len--){
next=head->next;
head->next=newhead;
newhead=head;
head=next;
}
tail->next=head;
if(pre_head==nullptr){
return newhead;
}else{
pre_head->next=newhead;
return result;
}
}
};
做题思路
- 先计算需要反转的链表长度len=right-left+1
- 记录链表的头节点(因为之后的操作,向后遍历会改变head的值,所以要将头节点备份)
- 从头开始遍历,得到反转链表头节点的前驱pre和需要开始反转的节点head
- 此刻的head是需要反转链表的头节点,也是反转后的尾结点,记录备份。
- 从head开始,反转指定长度len,newhead就是反转完成后的头节点,此时head是尾节点的后继,也就是不需要反转的第一个节点
- 之后将反转部分的尾结点tail与后面的节点head相连
- 根据判断前驱节点是否存在,若不存在(即从第一个节点开始)则返回反转链表头节点newhead,若存在(即开始反转的位置不是第一个节点)则将前驱与反转后的头节点newhead接好,再返回之前备份的头节点result
本题要注意的编程细节
Q:链表反转需要进行几次
A:有几个节点需要反转,就循环几次,也就是说每次循环,反转一个节点
Q:while(len–)和while(–len)有何区别
A:首先循环次数不一样,len–的时候循环进行len次,为len-1到0。–len的时,循环进行len-1次,为len-1到1。本题使用了–left,使得当left为1时,这种情况不会进入到循环之中,循环中的变量自然也不会发生改变。当完成整个操作以后,再判断循环中的值是否变化,就可以分开处理left是否为1的情况
Q:链表反转后,各变量指向什么节点
A:newhead会指向反转后的头节点,若以将head(由next赋值)遍历到结尾为条件循环,此刻的head就会指向null。若链表没有遍历结束,则head指向的是完成遍历后的链表部分的后一个节点,也就是现在的newhead节点在原链表中的后继节点。
Q:链表向后遍历的过程中,哪些变量需要备份
A:头节点head不断后移,题目若要求返回原始链表的头节点,需要将头节点备份
Q:链表进行反转之前,那些变量需要备份
A:原链表的头节点,反转后会变为链表的最后一个节点,所以可以在反转前将其备份tail=head。而循环结束时的head变量是反转完成链表的后继,所以用tail->next=head可以将链表后面连起来
Q:这道题是如何获得链表某个节点的前驱和后继的
A:使用一个循环,–left负责控制循环次数 循环中,将当前节点备份,再指向下一个。所以pre_head是前驱,head是当前的节点。后继是根据反转的过程,得到了未反转的第一个节点,也就是后继。那如何用相同的方法得到后继呢 tail就是当前节点,head就是后继
while(head&&right--){
tail=head;
head=head->next;
}
方法二
左神书上有另外一种做法,首先通过一次循环整个链表,获取链表的长度,头节点的前驱和尾节点的后继。还对输入的合法性作出判断。反转对应区间链表后,将前驱和头节点,尾节点和后继连接在一起。
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
int len=0;
ListNode* fpre=nullptr;
ListNode* tpos=nullptr;
ListNode* node1=head;
while(node1){
len++;
fpre=len==left-1?node1:fpre;
tpos=len==right+1?node1:tpos;
node1=node1->next;
}
if(left<1||left>right||right>len){
return head;
}
node1=fpre==nullptr?head:fpre->next;
ListNode* node2=node1;
ListNode* newhead=nullptr;
ListNode* next=nullptr;
while(node1!=tpos){
next=node1->next;
node1->next=newhead;
newhead=node1;
node1=next;
}
node2->next=tpos;
if(fpre==nullptr){
return newhead;
}else{
fpre->next=newhead;
return head;
}
}
};
代码细节
Q:获取链表的长度和指定节点的前驱后继
A:判断len与left-1或者right+1是否相等,若相等则说明正好在对应节点上,若不相等,则保持原来的值