LeetCode92反转部分链表

这篇博客详细解析了一道关于链表反转的编程题,介绍了如何在给定的两个节点之间反转链表。文章探讨了反转过程中的关键步骤,包括计算反转长度、保存节点引用、反转操作以及连接链表的前后部分。同时,讨论了编程细节,如循环次数、变量备份以及获取节点前驱和后继的方法。此外,还提供了两种不同的实现方案,强调了边界条件的处理和代码优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
        }
    }
};

做题思路

  1. 先计算需要反转的链表长度len=right-left+1
  2. 记录链表的头节点(因为之后的操作,向后遍历会改变head的值,所以要将头节点备份)
  3. 从头开始遍历,得到反转链表头节点的前驱pre和需要开始反转的节点head
  4. 此刻的head是需要反转链表的头节点,也是反转后的尾结点,记录备份。
  5. 从head开始,反转指定长度len,newhead就是反转完成后的头节点,此时head是尾节点的后继,也就是不需要反转的第一个节点
  6. 之后将反转部分的尾结点tail与后面的节点head相连
  7. 根据判断前驱节点是否存在,若不存在(即从第一个节点开始)则返回反转链表头节点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是否相等,若相等则说明正好在对应节点上,若不相等,则保持原来的值

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值