zoukankan      html  css  js  c++  java
  • 单链表的基本操作和实现

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

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

        

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

          

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

        

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

    (二)链表的存储结构

      (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;      //跳转到程序结束语句
    }

    (五)运行结果

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

        

  • 相关阅读:
    (转载)C++ string中find() ,rfind() 等函数 用法总结及示例
    UVA 230 Borrowers (STL 行读入的处理 重载小于号)
    UVA 12100 打印队列(STL deque)
    uva 12096 The SetStack Computer(STL set的各种库函数 交集 并集 插入迭代器)
    uva 1592 Database (STL)
    HDU 1087 Super Jumping! Jumping! Jumping!
    hdu 1176 免费馅饼
    HDU 1003 Max Sum
    转战HDU
    hust 1227 Join Together
  • 原文地址:https://www.cnblogs.com/953-zjf/p/LinkList.html
Copyright © 2011-2022 走看看