C语言 用迭代与递归分别实现单链表反转

一、前言

相信许多小可爱在刚接触链表的时候都会接触一个问题——如何不借助辅助链表实现单链表的反转,也许看到这里之前你一定很头疼,但是别急,看完这篇,你会发现链表竟是那么神奇

二、迭代法

1、算法原理

我们进行反转的思路是通过把原链表的头结点的下一个结点拎出来,然后让他成为新的头节点,当原来的头结点成为尾结点的时候就说明反转完成了。
在反转之前,我们假设链表是这样的
111
没错,我们要实现的是头节点存数据的单链表的反转,习惯头节点不存数据的小可爱也不必担心,基本原理是类似的,到时候根据原理自己写出来的时候相信你一定会感悟颇深的。
好的,回归正题。
我们首先做的一步工作非常的简单——先判断链表是否为空,如果是个空链表,那还反转个啥呀?直接咋来的咋给他返回回去,剩下的时间就可以摸鱼啦!(手动兴奋)
哈哈,开个玩笑,当链表不为空的时候,应该怎样操作呢?
首先,我们用一个flag记录下原来链表中的头节点。
在这里插入图片描述
紧接着,用一个newNode记录下flag的下一个元素,即flag->next,这就是我们这次循环要操作的元素了
在这里插入图片描述
接下来就到重头戏了,屏住呼吸,我们让flag->next=newNode->next,也就是把“2”这个结点从链表中删除掉。
在这里插入图片描述
将此结点从链表中先除掉是为了让他可以成为新的头节点而不影响其他结点。
在这里插入图片描述
之后我们让链表的头节点(head)指向这个被操作的结点,也就是“2”,既然操作之后此节点成为了新的头结点,那自然是要让头结点指向这个结点了呀。
在这里插入图片描述

自此,我们迭代的第一次成功完成了(👏)。
之后的操作就类似于以上操作,让“3”指向“2”,“4”指向“3”,“5”指向“4”……
那么问题来了,我们如何去判断何时我们的反转工作彻底结束呢?那我先反客为主一波,我先问一下各位,有没有想过为啥我要给一个变量命名为“flag”呢?
相信有结论的小可爱一定可以猜到上一个问题的答案了——没错,就是用flag作为循环的条件。
我们每操作一个结点之前,都会让flag的下一个结点指向被操作节点的下一个结点(比如操作“2”就让flag的下一个结点指向“3”),也就是flag->next=newNode->next。当被操作结点是原链表的最后一个结点的时候,就意味着我们的工作进行到最后一次迭代了,本次迭代中,flag->next将会等于NULL(单链表的最后一个结点的next就是NULL呀,不会还有小可爱不知道吧)。所以说我们只要判断是否flag->next==NULL就可以知道知道是否结束循环。
算法原理就是这些,接下来是代码实现:

2、代码实现

struct ListNode* reverseList(struct ListNode* head) {
    if (!head)      //判断链表是否为空,如果是空链表则直接返回
        return head;
    struct ListNode* flag = head;   //让flag指向当前原链表的结节点,用于之后找到要操作的结点以及判断循环是否结束
    while (flag->next)      //进入循环,当flag成为最后一个结点的时候退出循环
    {
        struct ListNode* newNode = flag->next;  //用于记录要操作的结点
        flag->next = newNode->next;             //将要操作的结点先从原链表中删除
        newNode->next = head;     //让被操作结点的next指向他的上一个结点,也就是当前的头节点
        head = newNode;           //让头节点指向被操作的结点
    }
    return head;       //返回头结点
}

二、递归法

1、算法原理

递归算法的思想是直接让链表的后一个结点指向他的前一个结点。是不是很惊讶,单链表不是只能向后遍历的吗,怎么能指向他的上一个结点呢?别急,看我的细节。
其实就是用了栈的思想(好像递归就i是栈哈,手动滑稽),让链表的每一个结点依次入栈,当检索到是最后一个结点的时候停止进栈,在出栈的时候就是从最后一个结点向第一个结点进行操作,并且每此都返回原链表的尾结点,最后就会把原链表的尾结点返回到主函数里面,因为反转之后的链表的头节点就是原链表的尾结点嘛。
在递归过程中首先,我们假设n个结点中从第k+1个结点到第n个结点都已经完成反转(意味着最先被操作的结点是最后一个结点)。
在这里插入图片描述

现在正处在第k个结点,要让他的第k+1个结点指向他的第k个结点,于是我们让k结点的next结点(就是k+1)的next结点指向k结点。没错,就是这么简单,是不是很神奇!
在这里插入图片描述
当然,也不是说就做到这一步就可以了,毕竟好事总是多磨的嘛!让我们想一想一些边界情况:比如当k为第一个结点的时候进行完这步操作就会是这样的:
在这里插入图片描述
聪明的你一定已经意识到问题的所在了,链表的尾结点本应该是要指向NULL的,但是现在却指向倒数第二个结点了,所以我们在上一步操作之后还要再加一步,让当前结点指向NULL。
在这里插入图片描述

这样当当前结点是第一个结点的时候他的下一个结点就是我们想要的NULL了
在这里插入图片描述
至此,一次递归完成。不过别忘了返回原链表的尾结点哦。

2、代码实现

struct ListNode* reverseList(struct ListNode* head) 
{
    if (head == NULL || head->next == NULL)     //当当前结点为原链表的尾结点时返回尾结点
    {
        return head;
    }
    struct ListNode* newHead = reverseList(head->next); //接收返回的尾结点,传入的参数是当前结点的下一个结点
    head->next->next = head;        //让当前结点下一个结点的下一个结点指向当前结点
    head->next = NULL;              //让当前结点指向NULL
    return newHead;                 //返回尾结点 
}

四、结束语

好像也没啥好说的了,不过毕竟是人生中第一篇博客,多少有点小激动,总感觉想说点什么。emn……希望各位可以从中有所收获,如果有大佬有建议或者发现文章中有不对的地方,也希望我这块砖可以引来美玉啊。最后的最后,都看到这里了,不如点个关注呗,等我火了,你就是我的老粉啦!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flow_shi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值