• 单链表的基本操作和实现


    (一)链表的定义和介绍在这里就不多介绍了,我们现在看文章大概最烦的就是一上来就长篇大论,这样直接劝退了很多人,所以我们选择从简。

        头节点: 是单链表的头,是一个特殊的节点,只有指针域,没有数据域。

        

        节点:由两部分构成,第一部分是数据域,存储的是该节点的内容,第二部分是指针域,用来存储下一节点的地址,通过改地址可以访问下一个节点。

          

        单链表:由头节点和若干节点组成的链表。

        

        这是一个有三个节点的链表。

    (二)链表的存储结构

      (1)从上篇文章我们得知顺序表在计算机中存储的位置是连续的,就像宿舍楼一层的房间,都是相邻的,但链表不一样,链表不一定是相邻的,只不过每一个节点都会存储下一个节点的地址。比如说,小明有小红的地址,小红呢有小白的地址,小白又有小兰的地址,小黑有小明的地址,他们五个是一个班的,所以他们之间就形成了一个关系:

      

    这就是一个单链表的结构,单是指只有前驱节点有后继节点的地址,只有单向性,当然也有双向链表,后面遇到了我们再讲。

    (2)上面是我们自己抽象化的一个链表,在计算机中的存储结构是下图:    

    (三)代码分析

    (1)头文件

    #include <iostream>     //基本的输入输出
    using namespace std;   //声明命名空间
    #include<string.h>     //字符串头文件

    (2)预处理

    #define OK 1
    #define ERROR 0
    #define ElemType int

    (3)创建节点,很明显,这里要用到结构体,结构体有两个成员,一个是数据域,一个是指针域。

    typedef struct LNode
    {
        struct LNode *next;   //节点指针域
        string data;          //节点数据域
    }*LinkList,LNode;          //一个是结构体指针,一个是结构体名称

    (4)初始化函数,创建一个头节点,并将指针域置空。

    ElemType Init_Linklist(LinkList &L)  //初始化链表
    {
        L = new LNode;                  //创建一个头节点
        L->next = NULL;                 //并将头节点的指针域置空
        return OK;
    }

    (5)输出链表函数,这个函数呢,是将单链表中的每个元素按顺序输出,为了美观,用"->"隔开。

       实现步骤:①定义一个节点指针,指向第一个节点。(注意:这里的第一个节点是头节点之后的第一个。)

            ②while判断当前节点是否为空,如果不是空,就输出该节点内容,然后指针下移,下面的if是最后一个节点不输出"->",这样链表看起来会更美观。

    void Printf_LNode(LinkList L)   //遍历输出链表内容
    {
        cout<<"链表内容:";
        LNode *p = L->next;            //节点指针,用来遍历节点
        while(p)
        {
            cout<<p->data;             //输出节点的数据域
            p = p->next;
            if(p)                     //判断下一个节点是否为空,如果是就不输出"->"
            {
                cout<<"->";
            }
        }
        cout<<endl;
    
    }

    (6)前插法建立链表,即每次在第一个节点的位置插入新节点。咱们先自己想一下这个过程,首先,我们要新建一个节点吧,然后将新节点的指针域指向下一个节点,再把上一个节点的指针域指向自己,不就插入成功了嘛。看一下图片:

        

       实现步骤:①输出提醒,并得到你要创建节点的个数n。

            ②创建一个节点指针,便于后面改指针的操作。

            ③初始化头节点,并将指针域置空。

            ④要创建n个节点,自然需要循环n次,首先开辟一个新节点,并让p指向新节点,将新节点的指针指向下一个节点,再让上一个节点指向自己,然后在输入该节点的内容。

            ⑤输出链表。

    void Create_LinkList_h(LinkList &L)    //前插法创建单链表
    {
        int n;
        cout<<"请输入你要创建的链表的结点个数:";
        cin>>n;
        LNode *p;
        L = new LNode;        //初始化头节点
        L->next = NULL;
        cout<<"将要采用头插法创建单链表,请逆序输入"<<n<<"个节点内容:";
        for(int i = 0;i < n;i++)    //循环创建节点
        {
            p = new LNode;
            p->next = L->next;
            L->next = p;
            cin>>p->data;
        }
        Printf_LNode(L);          //输出链表内容
    }

    (7)后插法建立链表,尾插法是将每个元素插在最后,故称为后插法。与前插法大致相同,不过比前插法多一个节点指针,首先新建一个节点,然后自己的指针域为空,因为自己已经是最后一个节点了,然后让上一个节点指向自己,然后插入成功。

        实现步骤:①首先得到你要输入的节点个数n

             ②初始化头节点

             ③定义一个节点指针r,并指向L

             ④循环n次,每一次都先申请一个节点,输入节点内容,因为是后插,所以每一个节点的指针域都指向空,然后再让上一个节点指向自己,然后r后移一个节点。

    void Create_LinkList_r(LinkList &L)   //尾插法创建链表
    {
        int n;                              //节点个数
        cout<<"请输入你要创建的链表的结点个数:";
        cin>>n;
        L = new LNode;                    //初始化头节点
        L->next = NULL;
        LNode *r = L;
        cout<<"将要采用尾插法创建单链表,请输入"<<n<<"个节点内容:";
        for(int i = 0;i < n;i++)    //循环创建节点
        {
            LNode *p;
            p = new LNode;
            cin>>p->data;
            p->next = NULL;
            r->next = p;
            r = p;
        }
        Printf_LNode(L);
    }

    (8)取值,通过输入的位置, 遍历链表,找到对应位置的值,然后赋值即可。

        实现步骤:①首先输入你要取得元素的位置

             ②定义一个新的节点指针,并指向头节点的下一个节点,同时呢也定义一个j,用来记录节点的值。

             ③用while循环,让指针连续下移到相应位置,同时判断j的值是否达到n。

             ④如果已经移到空节点了,或者说j已经大于n了,就表明越界了。

             ⑤如果没有越界,就赋值即可。

    ElemType Get_ElemType(LinkList L,string &name)
    {
        int n;                      //元素位置
        cout<<"请输入你要取得元素的位置:";
        cin>>n;
        LNode *p = L->next;    //新建节点指针,并指向下一个节点
        int j = 1;
        while(p&&j<n)         //指针移到要取得位置
        {
            p = p->next;
            j++;
        }
        if(!p || j>n)           //判断是否越界
        {
            return ERROR;
        }
        name = p->data;        //取值
    }

    (9)插入节点,其实也就是先开辟一个节点,然后让新节点指向下一个节点,再让新节点的上一个节点指向自己,就插入成功了。

        实现步骤:①首先得到你要插入的位置

             ②定义一个节点指针,指向第一个节点

             ③指针连续下移,移到n-1的节点。

             ④判断是否越界

             ⑤新建一个节点和一个指向新节点的指针s,然后输入插入的节点的值。

                ⑥改指针,将上一个节点的指针赋给新节点,然后让上一个节点指向自己,就插入成功了。

    ElemType Insert_LNode(LinkList &L)   //插入节点函数
    {
        int n;      //插入得位置
        cout<<"请输入你要插入的位置:";
        cin>>n;
        LNode *p = L->next;
        int j = 1;
        while(p&&j<(n - 1))   //新建节点指针,并指向下一个节点
        {
            p = p->next;
            j++;
        }
        if(!p || j > (n - 1))  //判断是否越界
        {
            return ERROR;
        }
        LNode *s;         //指向新建节点的指针
        s = new LNode;
        cout<<"请输入你要插入的元素:";
        cin>>s->data;    //输入新节点的数据域内容
        s->next = p->next;
        p->next = s;
        Printf_LNode(L);   //输出单链表
    
    }

     (10)删元素,其实跟之前插元素的实现差不多,都是先移到你要删除的位置,然后把你想要删除节点的指针域赋给前一个节点,然后释放节点即可。

        实现步骤:①输出一边当前链表的内容,然后挑选并得到你要删除的位置,同时定义一个节点指针q。

             ②定义一个节点指针指向第一个节点,并定义一个计数变量j。

             ③指针p连续下移到要删除的元素的前一个位置,然后在把该节点的指针域(q = p->next),赋给节点指针q,这样q就指向了我们要删除的节点,然后让q指向我们要删除的节点的下一个节                                        点,然后释放q节点即可。

    ElemType Del_LNode(LinkList &L)  //删除节点函数
    {
        int n;
        LNode *q;       //首先定义一个节点指针
        Printf_LNode(L);
        cout<<"请输入你要删除的位置:";
        cin>>n;
        LNode *p = L->next;
        int j = 1;
        while(p&&(j< n - 1))  //指针下移
        {
            p = p->next;
            ++j;
        }
        if(!p || j > (n - 1))  //判断是否越界
        {
            return ERROR;
        }
        q = p->next;
        p->next = q->next;
        delete q;
        Printf_LNode(L);
    }

    (11)菜单函数,只是一些普通的输出,就不用解释了吧

    void Menu()   //菜单函数
    {
        cout<<"<<<<<<<<<<<<<<<菜单>>>>>>>>>>>>>>>"<<endl;
        cout<<"1.初始化单链表"<<endl;
        cout<<"2.头插法创建一个单链表"<<endl;
        cout<<"3.尾插法创建一个单链表"<<endl;
        cout<<"4.取单链表中的某个元素"<<endl;
        cout<<"5.在单链表某个位置插入元素"<<endl;
        cout<<"6.在单链表删除某个位置的元素"<<endl;
        cout<<"7.输出链表内容"<<endl;
        cout<<"8.菜单"<<endl;
        cout<<"9.退出系统!"<<endl;
        cout<<"输入对应选项,执行对应操作"<<endl;
    }

    (12)主函数:首先创建一个链表指针,输出菜单,然后进入while循环,通过switch输入不同的选项进入不同的case,然后执行不同的函数,这里为了能够退出系统(跳出循环)使用了goto语句来结束循环。这里需要特别说明的可能就是case 4和case 5,定义了两个标志变量,用来记录程序退出的值,看是否出错

    int main()
    {
        LinkList La;  //链表指针
        int select;
        Menu();
        while(1)   //循环输入选项
        {
            cout<<"请输入选项:";
            cin>>select;
            switch(select)   //判断选项,并执行对应的函数
            {
            case 1:
                {
                    Init_Linklist(La);
                    cout<<"初始化后单链表为空,要想进行操作,请先创建一个单链表"<<endl;
                    Create_LinkList_h(La);
                    break;
                }
            case 2:
                {
                    Create_LinkList_h(La);
                    break;
                }
            case 3:
                {
                    Create_LinkList_r(La);
                    break;
                }
            case 4:
                {
                    int flag;         //标志变量,用来记录退出码,用来判断是否为正常退出
                    string name;
                   flag = Get_ElemType(La,name);
                   if(!flag)
                   {
                       cout<<"取出失败,越界!"<<endl;
                   }
                    cout<<name<<endl;
                    break;
                }
            case 5:
                {
                    int flag;
                    flag = Insert_LNode(La);    //标志变量,用来记录退出码,用来判断是否为正常退出
                    if(!flag)
                    {
                        cout<<"插入失败,越界!"<<endl;
                    }
                    break;
                }
            case 6:
                {
                    Del_LNode(La);
                    break;
                }
            case 7:
                {
                    Printf_LNode(La);
                    break;
                }
            case 8:
                {
                    Menu();
                    break;
                }
            case 9:
                {
                    cout<<"多谢使用"<<endl;
                    goto unloop;      //使用goto语句,跳转,使程序结束
                }
            default:
            {
                cout<<"输入有误!"<<endl;
                break;
            }
            }
        }
        unloop:return 0;      //跳转到程序结束语句
    }

    (四)完整代码

    #include <iostream>     //基本的输入输出
    using namespace std;   //声明命名空间
    #include<string.h>     //字符串头文件
    
    #define OK 1
    #define ERROR 0
    #define ElemType int
    
    
    typedef struct LNode
    {
        struct LNode *next;   //节点指针域
        string data;          //节点数据域
    }*LinkList,LNode;          //一个是结构体指针,一个是结构体名称
    
    ElemType Init_Linklist(LinkList &L)  //初始化链表
    {
        L = new LNode;                  //创建一个头节点
        L->next = NULL;                 //并将头节点的指针域置空
        return OK;
    }
    
    void Printf_LNode(LinkList L)   //遍历输出链表内容
    {
        cout<<"链表内容:";
        LNode *p = L->next;            //节点指针,用来遍历节点
        while(p)
        {
            cout<<p->data;             //输出节点的数据域
            p = p->next;
            if(p)                     //判断下一个节点是否为空,如果是就不输出"->"
            {
                cout<<"->";
            }
        }
        cout<<endl;
    
    }
    
    void Create_LinkList_h(LinkList &L)    //前插法创建单链表
    {
        int n;
        cout<<"请输入你要创建的链表的结点个数:";
        cin>>n;
        LNode *p;
        L = new LNode;        //初始化头节点
        L->next = NULL;
        cout<<"将要采用头插法创建单链表,请逆序输入"<<n<<"个节点内容:";
        for(int i = 0;i < n;i++)    //循环创建节点
        {
            p = new LNode;
            p->next = L->next;
            L->next = p;
            cin>>p->data;
        }
        Printf_LNode(L);          //输出链表内容
    }
    
    void Create_LinkList_r(LinkList &L)   //尾插法创建链表
    {
        int n;                              //节点个数
        cout<<"请输入你要创建的链表的结点个数:";
        cin>>n;
        L = new LNode;                    //初始化头节点
        L->next = NULL;
        LNode *r = L;
        cout<<"将要采用尾插法创建单链表,请输入"<<n<<"个节点内容:";
        for(int i = 0;i < n;i++)    //循环创建节点
        {
            LNode *p;
            p = new LNode;
            cin>>p->data;
            p->next = NULL;
            r->next = p;
            r = p;
        }
        Printf_LNode(L);
    }
    
    ElemType Get_ElemType(LinkList L,string &name)
    {
        int n;                      //元素位置
        cout<<"请输入你要取得元素的位置:";
        cin>>n;
        LNode *p = L->next;    //新建节点指针,并指向下一个节点
        int j = 1;
        while(p&&j<n)         //指针移到要取得位置
        {
            p = p->next;
            j++;
        }
        if(!p || j>n)           //判断是否越界
        {
            return ERROR;
        }
        name = p->data;        //取值
    }
    
    ElemType Insert_LNode(LinkList &L)   //插入节点函数
    {
        int n;      //插入得位置
        cout<<"请输入你要插入的位置:";
        cin>>n;
        LNode *p = L->next;
        int j = 1;
        while(p&&j<(n - 1))   //新建节点指针,并指向下一个节点
        {
            p = p->next;
            j++;
        }
        if(!p || j > (n - 1))  //判断是否越界
        {
            return ERROR;
        }
        LNode *s;         //指向新建节点的指针
        s = new LNode;
        cout<<"请输入你要插入的元素:";
        cin>>s->data;    //输入新节点的数据域内容
        s->next = p->next;
        p->next = s;
        Printf_LNode(L);   //输出单链表
    
    }
    
    ElemType Del_LNode(LinkList &L)  //删除节点函数
    {
        int n;
        LNode *q;       //首先定义一个节点指针
        Printf_LNode(L);
        cout<<"请输入你要删除的位置:";
        cin>>n;
        LNode *p = L->next;
        int j = 1;
        while(p&&(j< n - 1))  //指针下移
        {
            p = p->next;
            ++j;
        }
        if(!p || j > (n - 1))  //判断是否越界
        {
            return ERROR;
        }
        q = p->next;
        p->next = q->next;
        delete q;
        Printf_LNode(L);
    }
    void Menu()   //菜单函数
    {
        cout<<"<<<<<<<<<<<<<<<菜单>>>>>>>>>>>>>>>"<<endl;
        cout<<"1.初始化单链表"<<endl;
        cout<<"2.头插法创建一个单链表"<<endl;
        cout<<"3.尾插法创建一个单链表"<<endl;
        cout<<"4.取单链表中的某个元素"<<endl;
        cout<<"5.在单链表某个位置插入元素"<<endl;
        cout<<"6.在单链表删除某个位置的元素"<<endl;
        cout<<"7.输出链表内容"<<endl;
        cout<<"8.菜单"<<endl;
        cout<<"9.退出系统!"<<endl;
        cout<<"输入对应选项,执行对应操作"<<endl;
    }
    
    int main()
    {
        LinkList La;  //链表指针
        int select;
        Menu();
        while(1)   //循环输入选项
        {
            cout<<"请输入选项:";
            cin>>select;
            switch(select)   //判断选项,并执行对应的函数
            {
            case 1:
                {
                    Init_Linklist(La);
                    cout<<"初始化后单链表为空,要想进行操作,请先创建一个单链表"<<endl;
                    Create_LinkList_h(La);
                    break;
                }
            case 2:
                {
                    Create_LinkList_h(La);
                    break;
                }
            case 3:
                {
                    Create_LinkList_r(La);
                    break;
                }
            case 4:
                {
                    int flag;         //标志变量,用来记录退出码,用来判断是否为正常退出
                    string name;
                   flag = Get_ElemType(La,name);
                   if(!flag)
                   {
                       cout<<"取出失败,越界!"<<endl;
                   }
                    cout<<name<<endl;
                    break;
                }
            case 5:
                {
                    int flag;
                    flag = Insert_LNode(La);    //标志变量,用来记录退出码,用来判断是否为正常退出
                    if(!flag)
                    {
                        cout<<"插入失败,越界!"<<endl;
                    }
                    break;
                }
            case 6:
                {
                    Del_LNode(La);
                    break;
                }
            case 7:
                {
                    Printf_LNode(La);
                    break;
                }
            case 8:
                {
                    Menu();
                    break;
                }
            case 9:
                {
                    cout<<"多谢使用"<<endl;
                    goto unloop;      //使用goto语句,跳转,使程序结束
                }
            default:
            {
                cout<<"输入有误!"<<endl;
                break;
            }
            }
        }
        unloop:return 0;      //跳转到程序结束语句
    }

    (五)运行结果

     程序在健壮性方面还有很多不足,各位大神如果有什么好的想法或者发现我的某些错误,欢迎指正!最后希望大家给我一个支持,谢谢!

        

  • 相关阅读:
    Spark2 Dataset DataFrame空值null,NaN判断和处理
    Spark2 文件处理和jar包执行
    &与&&, |与||区别
    Scala实现乘法口诀
    Hive desc
    Hive FUNCTIONS函数
    Hive show
    MySQL行列转换拼接
    MySQL字符串连接
    SQLServer之索引简介
  • 原文地址:https://www.cnblogs.com/953-zjf/p/LinkList.html
Copyright © 2020-2023  润新知