删除单链上数据域值最小的节点_数据结构(二)| 一文搞懂单链表的基本操作和实现...

https://www.cnblogs.com/953-zjf/p/LinkList.html

(一)链表的几个基本概念

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

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

2c368e8b80ef843f414e76731ad30415.png

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

8b90e7c32866c7090b69b11c8eef708c.png

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

(二)链表的存储结构

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

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

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

(三)代码分析

(1)头文件
#include      //基本的输入输出using namespace std;   //声明命名空间#include     //字符串头文件

(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<data;             //输出节点的数据域        p = p->next;        if(p)                     //判断下一个节点是否为空,如果是就不输出"->"        {            cout<<"->";        }    }    cout<<endl;}
(6)前插法建立链表,即每次在第一个节点的位置插入新节点。咱们先自己想一下这个过程,首先,我们要新建一个节点吧,然后将新节点的指针域指向下一个节点,再把上一个节点的指针域指向自己,不就插入成功了嘛。看一下图片: ab5be752a157690ec70cf0134aee31be.png 实现步骤: ①输出提醒,并得到你要创建节点的个数n。 ②创建一个节点指针,便于后面改指针的操作。 ③初始化头节点,并将指针域置空。 ④要创建n个节点,自然需要循环n次,首先开辟一个新节点,并让p指向新节点,将新节点的指针指向下一个节点,再让上一个节点指向自己,然后在输入该节点的内容。 ⑤输出链表。
void Create_LinkList_h(LinkList &L)    //前插法创建单链表{    int n;    cout<<"请输入你要创建的链表的结点个数:";    cin>>n;    LNode *p;    L = new LNode;        //初始化头节点    L->next = NULL;    cout<<"将要采用头插法创建单链表,请逆序输入"<"个节点内容:";    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后移一个节点。 6eaec41932cd89cf39de2af1746b808c.png
void Create_LinkList_r(LinkList &L)   //尾插法创建链表{    int n;                              //节点个数    cout<<"请输入你要创建的链表的结点个数:";    cin>>n;    L = new LNode;                    //初始化头节点    L->next = NULL;    LNode *r = L;    cout<<"将要采用尾插法创建单链表,请输入"<"个节点内容:";    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//指针移到要取得位置    {        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&&j1))       {        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节点即可。 81f991b1051e10f6e3b2c972137e4948.png
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<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      //基本的输入输出using namespace std;   //声明命名空间#include     //字符串头文件#define OK 1#define ERROR 0#define ElemType inttypedef 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<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<<"将要采用头插法创建单链表,请逆序输入"<"个节点内容:";    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<<"将要采用尾插法创建单链表,请输入"<"个节点内容:";    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//指针移到要取得位置    {        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&&j1))       {        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<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;      //跳转到程序结束语句}
(五)运行结果 1a2fedb65592f5b210097ad0ff7a2029.png 程序在健壮性方面还有很多不足,各位大神如果有什么好的想法或者发现我的某些错误,欢迎指正!最后希望大家给我一个支持,谢谢!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值