结构体 链表小例子

http://c.chinaitlab.com/cc/ccjq/200806/751691_2.html

程序既然是循环输入,而结构成员test *next又是用来存储下一个接点的内存地址的,每次循环我们又要动态创建一个新的内存空间,所以我们必须要有一个指针来存储上一次循环动态开辟的内存地址,于是就有了

    test *le;

      接下来在进入循环前我们要创建链表的第一个节点,第一个节点必然是在循环外创建,于是就有了

    cin>>ls->name>>ls->socre;

      程序执行者的情况是位置的,所以我们必然要考虑,一上来就不想继续运行程序的情况,所以我们一开始先把head引导指针设置为不指向任何地址也就是

    head=NULL;

      为了符合le也就是链尾指针的设计思路,我们在循环前一定要保存刚刚动态开辟的内存地址,好在下一次循环的时候设置上一个节点中的next成员指向,于是我们便有了:

    le=ls;

      为了实现循环输入我们又了下面的代码:

    while(strcmp(ls->name,"null")!=0)
    {
    if(head==NULL)
    {
    head=ls;
    }
    else
    {
    le->next=ls;
    }
    le=ls;
    ls=new test;
    cin>>ls->name>>ls->socre;
    }

      程序是循环必然要有终止循环的条件,所以我们的循环条件是:

    while(strcmp(ls->name,"null")!=0)

      输入的名字是null的时候就停止循环。

      为了保证第一次进入循环,也就是在循环内准备创建第二个节点前,设置引导指针的指向我们有了如下的判断代码:

    if(head==NULL)
    {
    head=ls;
    }
    else
    {
    le->next=ls;
    }

 


      代码中的else条件是为了设置前一个节点next指向而写的,这点我们记住先看下面的代码,稍后大家回过头想就明白了

    le=ls;
    ls=new test;
    cin>>ls->name>>ls->socre;

      le=ls;这么写就是为了保存上一次循环指针的位置而设的,正是为了上面的else代码而做的预先保留

    ls=new test;
    cin>>ls->name>>ls->socre;

      这两行代码的意思就是继续开辟下一个节点空间,和输入节点内容!

      循环一旦结束也就结束了程序,为了保持程序不出错,也就是最后一个节点的next成员指向为空我们有了下面的代码

    le->next=NULL;

      程序的思路始终是以先开辟后判断为思路的,所以到最后一个不成立的时候总会有一个多开辟的内存空间,为了删除掉它,我们有了下面的代码

    delete ls;

      程序到最后由于返回head指针

    return head;

      显示链表的函数没有什么太多特别的也只需要注意下面这样就可以了!

    head=head->next;

      我们之所以不用head+=1;来写就是因为链表是我们动态开辟的,而每一个节点的位置并不是相连的,next成员指针的意义也就是下一个节点的内存地址。

      到这里整个创建函数的设计思路也都说完了,笔者不一定说的很好,但基本思路是这样的,希望读者多思考,多对比,相信此教程还是对大家有帮助的,程序设计就是利用逐步思考的方式进行的,写好的代码往往直接看看不懂就是因为中间的细节并不是一次都能够想到的。

      下面我们来说一下链表节点的删除!

      我们以上面的程序为基础,但为了我们方便学习删除我们休整结构体为

    struct test
    {
    int number;
    float socre;
    test *next;
    };

      number为唯一的编号每一个节点的。

      删除的我就不多说了,里面重要部分有注解。

      特别注意deletel函数的参数意义,指针的引用在这里很重要,如果只是指针,或者只是应用都是不行的,为什么仔细思考,很多知名的教材在这一问题上都很模糊,而且很多书还有错误,程序不错,但思路是错的,我这里特别不说,请大家仔细阅读程序,如果还是有问题,可以回此帖,我会回答的。

 


      完整代码如下:

    #include <iostream>
    using namespace std;
    struct test
    {
        int number;
        float socre;
        test *next;
    };
    test *head;//创建一个全局的引导进入链表的指针

    test *create()
    {
        test *ls;//节点指针
        test *le;//链尾指针
        ls = new test;//把ls指向动态开辟的堆内存地址
        cin>>ls->number>>ls->socre;
        head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
        le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置
        while(ls->number!=0)//创建循环条件为ls->number的值不是null,用于循环添加节点
        {
            if(head==NULL)//判断是否是第一次进入循环
            {
                head=ls;//如果是第一次进入循环,那么把引导进入链表的指针指向第一次动态开辟的堆内存地址
            }
            else
            {
                le->next=ls;//如果不是第一次进入那么就把上一次的链尾指针的le->next指向上一次循环结束前动态创建的堆内存地址
            }
            le=ls;//设置链尾指针为当前循环中的节点指针,用于下一次进入循环的时候把上一次的节点的next指向上一次循环结束前动态创建的堆内存地址
            ls=new test;//为下一个节点在堆内存中动态开辟空间
            cin>>ls->number>>ls->socre;
        }
        le->next=NULL;//把链尾指针的next设置为空,因为不管如何循环总是要结束的,设置为空才能够在循环显链表的时候不至于死循环
        delete ls;//当结束的时候最后一个动态开辟的内存是无效的,所以必须清除掉
        return head;//返回链首指针
    }
    void showl(test *head)
    {
        cout<<"链首指针:"<<head<<endl;
        while(head)//以内存指向为null为条件循环显示先前输入的内容
        {
            cout<<head->number<<"|"<<head->socre<<endl;
            head=head->next;
        }
    }
    void deletel(test *&head,int number)//这里如果参数换成test *head,意义就完全不同了,head变成了复制而不是原有链上操作了,特别注意,很多书上都不对这里
    {
        test *point;//判断链表是否为空
        if(head==NULL)
        {
            cout<<"链表为空,不能进行删除工作!";
            return;
        }
        if(head->number==number)//判删除的节点是否为首节点
        {
            point=head;
            cout<<"删除点是链表第一个节点位置!";
            head=head->next;//重新设置引导指针
            delete point;
            return;
        }
        test *fp=head;//保存连首指针
        for(test *&mp=head;mp->next;mp=mp->next)
        {
            if(mp->next->number==number)
            {
                point=mp->next;
                mp->next=point->next;
                delete point;
                head=fp;//由于head的不断移动丢失了head,把进入循环前的head指针恢复!
                return;
            }
        }
    }
    void main()
    {
        head=create();//调用创建
        showl(head);
        int dp;
        cin>>dp;
        deletel(head,dp);//调用删除
        showl(head);
        cin.get();
        cin.get();
    }

 


      最后我学习一下如何在已有的链表上插入节点

      我们要考虑四中情况,

      1.链表为空!

      2.插入点在首节点前

      3.插入点找不到的情况我们设置放在最后!

      4.插入点在中间的情况!

      今天的程序在昨天的基础上做了进一步的修改,可以避免删除点找不到的情况,如果找不到删除点就退出函数!

    #include <iostream>
    using namespace std;
    struct test
    {
        int number;
        float socre;
        test *next;
    };
    test *head;//创建一个全局的引导进入链表的指针

    test *create()
    {
        test *ls;//节点指针
        test *le;//链尾指针
        ls = new test;//把ls指向动态开辟的堆内存地址
        cout<<"请输入第一个节点number和节点score,输入0.0跳出函数"<<endl;
        cin>>ls->number>>ls->socre;
        head=NULL;//进入的时候先不设置head指针指向任何地址,因为不知道是否一上来就输入null跳出程序
        le=ls;//把链尾指针设置成刚刚动态开辟的堆内存地址,用于等下设置le->next,也就是下一个节点的位置
        while(ls->number!=0)//创建循环条件为ls->number的值不是null,用于循环添加节点
        {
            if(head==NULL)//判断是否是第一次进入循环
            {
                head=ls;//如果是第一次进入循环,那么把引导进入链表的指针指向第一次动态开辟的堆内存地址
            }
            else
            {

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值