首次体验单向链表的翻转(c语言实现)

原本这个方法还没有学到,凑巧碰到一道题偏偏要用,索性自己先写一个试试吧,结果就开始了痛苦的历程

首次设计

链表翻转就是要把原来的表尾变成表头,如果使用的是带头结点的链表,那就要将原来的头节点的指向换成表尾,或者也可以选择释放掉原来的头节点,分配一个新的头节点,我在这里选择了后一种,因为我太菜了,只想着先实现出来。(优化的过程在后面),头指针翻转之后就是整个链表方向的反转,在遍历整个链表的同时实现指针的翻转,先看一下我第一次想到的方法

这是原始链表(带头节点)

在这里插入图片描述
所以按照我所选择的方法,首先要释放头节点,让表头变表尾(当然也可以留着最后直接指向尾部)

释放头节点在这里插入图片描述

当然在释放之前不要忘记建立一个指针指向首元节点,不然这个链表可就丢了,这里我用了三个指针指向了后续的三个节点,这里提前解释一下,如果要实现翻转,至少是需要三个指针的,所以我就在这里直接让他们指向了后续的三个节点(这里我总是想让三个指针始终指向不同的节点,所以导致了我的方法十分的复杂),释放头节点之后可以顺便将节点一的指针域置空了(这里我画图的时候忘记啦哈哈哈啊)。
那么准备工作到此结束,接下来就是遍历整个链表进行反转了,所以接下来的操作都是在遍历的循环体内实现的。

翻转开始

在这里插入图片描述
我们利用指针二,将节点二的指针域指向节点一
在这里插入图片描述
这是指针一的认为就完成了,我们让他向后移动,指向我们后续需要操作的节点,
然后继续我们的翻转操作在这里插入图片描述
利用指针三,将节点三的指针域指向节点二
在这里插入图片描述同样,我们再次让指针二向后移动,貌似就是这么不断重复的规律,我们继续试试看在这里插入图片描述
利用指针一继续翻转
在这里插入图片描述
然后指针再次移动,这个时候我们发现,现在的状态回到了我们最初开始翻转的状态,也就是说形成了一个循环,后续的操作便是不断地重复这一个过程。
好了,到此已经找到了循环操作的方法了

那么什么时候停止呢?

当然是找到表尾的时候停止,但是我们在一次循环的过程中,指针总共移动了三次,其中的每一次都可能是找到表尾,所以我们每一次移动指针,都要检查一下他是否是指向表尾了,因此在上面这一个循环中,我们要检查三次。

那就写三个if呗,发现表尾就跳出嘛

是的,三个判断,找到表尾就跳出,但是我们也发现了另外一个问题

那就是在一次循环中,我们翻转了三个节点,也就意味着,当我们找到表尾时,后面一定有有两个节点相对指向还是原来的方向,没有被我们翻转,所以我们跳出循环之后, 要执行一个收尾。
下面贴出代码

//链表反转(带头节点)  自己写的,真鸡儿长

typedef struct LNode *List;
typedef struct LNode *PtrToNode;
typedef struct LNode *Position;

struct LNode
{
    ElementType Data;
    PtrToNode Next;
};
List reversal(List l)
{
    Position newHead,tempPtr_1,tempPtr_2,tempPtr_3;
    tempPtr_1 = l->Next;//指向首元节点
    tempPtr_2 = tempPtr_1->Next;
    tempPtr_3 = tempPtr_2->Next;
    free(l);//释放头节点
    l = NULL;
    tempPtr_1->Next = NULL;//首元节点变尾节点
    while(1)//进行反转,直到尾节点
    {
        tempPtr_2->Next = tempPtr_1;//反转
        tempPtr_1 = tempPtr_3->Next;//移动
        if(tempPtr_1 == NULL)
        {
            break;
        }
        tempPtr_3->Next = tempPtr_2;//反转
        tempPtr_2 = tempPtr_1->Next;//移动
        if(tempPtr_2 == NULL)
        {
            break;
        }
        tempPtr_1->Next = tempPtr_3;//反转
        tempPtr_3 = tempPtr_2->Next;//移动
        if(tempPtr_3 == NULL)
        {
            break;
        }
    }
    newHead = (List)malloc(sizeof(struct LNode));
    if(tempPtr_1 == NULL)
    {
        tempPtr_3->Next = tempPtr_2;
        newHead->Next = tempPtr_3;//将尾节点赋给新头节点
    }
    if(tempPtr_2 == NULL)
    {
        tempPtr_1->Next = tempPtr_3;
        newHead->Next = tempPtr_1;//将尾节点赋给新头节点
    }
    if(tempPtr_3 == NULL)
    {   
        tempPtr_2->Next = tempPtr_1;
        newHead->Next = tempPtr_2;//将尾节点赋给新头节点
    }
    
    newHead->Next = tempPtr;//将尾节点赋给新头节点
    return newHead;
}

上面的代码之所以会出现这种问题,是因为每次循环都移动了三次指针,翻转了三个节点,所以如果能让每次循环都只翻转一个节点,就可以免去这复杂的判断。
我们来尝试着设计一下

必须使用三个节点才能实现

这是已经验证的,一定要使用三个节点,而如果每次只让他们指向两个节点,就意味着有重复的指针。
同时这次我们把头节点也利用起来

首先我们传入链表后,先将首元节点转换为尾节点

在这里插入图片描述
然后我们就可以开始链表的翻转
首先在翻转节点二之前,我们要让temp指针指向节点三,将下一个节点的位置保存下来
在这里插入图片描述
然后我们翻转节点二
在这里插入图片描述
节点二翻转完成后,要将指针后移。但是要注意移动的顺序,如果我们先移动after,那么pre指针就无法后移了,所以我们先移动pre,再移动after
在这里插入图片描述
这样一次节点翻转就完成了,这样我们每次循环就可以只翻转一个节点,只向后寻找一个节点,就可以免除上面的复杂判断
这样不断地重复上述过程,直到遍历到最后一个节点,即temp指向空时,翻转就结束了,然后只要把原本的头节点指向pre,就实现了链表的翻转
下面贴出完整代码

List reversal(List l)
{
    /*
    定义三个指针,pre始终保持在after之前一个几点,两者配合实现两节点之间的翻转,而temp指针则负责保存后续按钮,避免翻转之后丢失后续链表
    */
    Position pre,after,temp;
    pre = l->Next;//指向首元节点
    after = pre->Next;//指向第二个节点
    temp = after;//做临时存储之用
    pre->Next = NULL;//首元节点变尾节点
    /*
    此时,三个指针仅仅指向两个节点
    */
    if(after == NULL)//只有一个节点时,无法翻转
    {
        return l;
    }
    //每次只翻转一个节点
    while(temp != NULL)
    {
        temp = after->Next;//向后保存一个节点,after翻转后丢失链表
        after->Next = pre;//翻转
        pre = after;//pre指针后移一位
        after = temp;//after后移一位
    }
    //循环结束后,after和temp指向空,pre指向尾节点
    l->Next = pre;//头节点指向尾部
    return l;//其实也可以定义为void返回类型,毕竟是传进来的是指针
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值