单链表OJ题练习-Leetcode 206.反转链表

本文探讨了使用迭代法和头插法两种方法实现链表反转,重点解决了迭代法中的内存泄漏问题,并强调了在头插法中处理链表为空或只有一个节点时的边界条件。

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

题目:

接口:

原链表

这里我用了两种来解决。

方法一.迭代法

思路:

        既然要使链表反转,那么原本n1->next=n2,n1指向n2(即n1存储n2的地址),就反转成n2指向n1(即n2存储n1的地址),这样两个节点就可以反转了。

        但这样会出现一个问题,n2->next存储的是n1的地址,却丢失了原本下一个节点的地址(即原来指向该节点的指针丢失了),会造成内存泄漏导致该节点无法访问,所以需要一个n3来保存下一个节点地址。

错误以及逆转如图

        解决方法:再用一个指针n3在进行逆转前存储下一个节点的地址,n3=n2->next将下一个节点地址存储防止丢失,n3做到的就是帮助n2找到下一个节点,并且还能通过n3遍历逆转。

如图:

        遍历迭代过程,根据我自己的习惯,喜欢画图进行分析,如下方GIF可以清晰得看出递归逆转的过程,就需要将n2的地址赋给n1,n3的地址赋给n2,n3存储下一个节点则n3=n2->next,当n2下一个节点地址存储好后,这时再进行逆转,

        以上应该能够知道如何递归逆转,那何时结束呢?

        是当n2==NULL的时候,return n1,当n2==NULL的时候n1才到最后一个节点的位置,n1才存储下最后一个位置。

        代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL || head->next == NULL){
        return head;
    }
    struct ListNode* n1 = NULL;
    struct ListNode* n2 = head;
    struct ListNode* n3 = n2->next;
    while(n2){
        //存储下一个节点的地址
        n3 = n2->next;
        //逆转
        n2->next = n1;
        //迭代
        n1 = n2;
        n2 = n3;
    }
    return n1;
}

方法二.头插法

思路:

        在单链表初步学习中学习到了头插,将这思路运用到逆转中,从头节点开始向新链表中依次头插,就能够达到反转的效果

        将原链表从head开始头插进新链表中,但也就意味着原本存储原链表下一个节点地址的指针指向了新链表,那么意味着下一个节点内存泄露,丢失其地址。

        所以需要next来存储下一个节点的地址。

        由此我们可知我们需要三个指针,cur记录当前需要头插的节点,next记录下一个节点,newNode为新链表的尾指针所以为NULL                                                                                                                                             。

    struct ListNode* cur = head;
    struct ListNode* newNode = NULL;

        将cur头插到newNode中,需要连接newNode与cur(对头插还有疑惑的,多看下,多画下单链表头插的过程)。头插完成,根据题目最后要输出反转后的链表的头指针,所以newNode也要前进,进行遍历,先将newNode移至已经头插完毕的节点处,通过next将cur移至下一个节点,next再存储下一个节点的位置。

图像化展示

    while(cur){
        cur->next = newNode;
        newNode = cur;
        cur = next;
        next = next->next;
    }

                                      

        ▲注意▲,遍历反转循环结束的条件时cur==NULL(不以next=NULL为条件判断,当next==NULL时,还没头插最后一个)就要注意,next一直都是cur下一个节点,那么当cur==NULL时,next再继续访问,会出现段错误,尝试访问NULL指针所指向的内存是非法的。

所以需要加一个判断这段循环应该是这样

    while(cur){
        cur->next = newNode;
        newNode = cur;
        cur = next;
        if(next){
            next = next->next;
        }
    }

        ▲没有节点和只有一个节点也不会有问题,如果head==NULL那么循环就不会开始,返回的newNode也是NULL,只有一个节点进入循环,也就是头插一个节点,返回也没问题。

        这就对了吗 no ! No! ! NO! ! !

        这里由于个人习惯,我将next的声明在循环外,也就是

    struct ListNode* cur = head;
    struct ListNode* newNode = NULL;
    struct ListNode* next =cur->next;

        这里就会出现一个错误,如果链表为空呢,或者只有一个节点呢,会出现空指针错误,那么next就是访问NULL指针所指向的内存。

        所以应该定义在循环之内,综上就可以得出完整的代码如下

完整代码        

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* cur = head;
    struct ListNode* newNode = NULL;
    

    while(cur){
        struct ListNode* next = cur->next;
        cur->next = newNode;
        newNode = cur;
        cur = next;
        if(next){
        next = next->next; 
        }
    }
    return newNode;
}

                                             

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值