数据结构—单链表的基本功能实现

链表的基本功能实现

  在前面的顺序表中,表结构简单,可以存取表中任一位置元素。但插入删除时需要移动大量元素。原因是顺序表中的数据元素逻辑上相邻,物理位置上也相邻。线性表的另一种链式存储结构—链表,它不要求逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序表的弱点,但也失去了顺序表的可随机存取的优点。顺序表可以随便访问任何一个元素,只需要进行表地址和索引地址相加即可访问该元素。而链表必须从第一个开始一个一个遍历找到。

  • 链表的结点中包含了指针域和数据域,由于每个结点只包含一个指针域,故又称为线性链表或单链表。

  • 单链表的存取必须从头指针开始进行,最后一个数据元素没有后继,指针为空。数据元素之间的逻辑关系由结点中的指针指示。

  • 有时我们会在单链表的第一个结点前附设一个头结点。头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等类的附加信息;头结点的指针域存储指向第一个结点的指针(即第一个元素结点的存储位置)。单链表的头指针指向头结点,若线性表为空表,则头结点的指针域为空。

  • 下面是单链表的实现实例:

     1.创建线性表:
     a.该线性表可以由用户输入创建大小
     b.由用户输入数据
     c.打印线性表
     
     2.依次为线性表添加 "增删改查" 的功能:
     a.输入要增加的数据,增加完后遍历打印
     b.输入要删除的数据,删除完后遍历打印
     c.输入要修改的数据,修改完后遍历打印
     d.输入要查找的数据,查找并打印出来
    
源代码如下:
#include <iostream>

using namespace std;

//定义线性表的结构
struct seqList
{
    int num;   //学生的学号
    int score;   //学生的分数
    struct seqList *next;   //注意:next它只是存放和该结点相链接的下一个结点的地址
};

struct seqList *node_head;          //定义头指针,控制住线性表的表头
struct seqList *node_end;            //定义尾指针,控制住线性表的表尾
struct seqList *node_increase;     //定义新增结点指针,用于操作线性表

void creatList(int n);                   //创建线性表的规模并录入数据
void printList(int n);                   //遍历打印线性表
void increaseNode(int n, int increase_n);             //新增结点
void deleteNode(int num, int n, int increase_n);  //删除指定学号的结点
void searchNode(int num,int n,int increase_n);   //查找对应学号的结点信息
void updateNode(int num,int n,int increase_n);  //修改对应学号的结点信息

int main()
{
    int n,num1,inc,num2,num3;
    cout<<"请输入链表的规模:";
    cin>>n;
    creatList(n);     //调用建表函数并打印链表信息
    cout<<"请输入要增加的结点个数:";
    cin>>inc;
    increaseNode(n,inc);  //调用新增结点函数并打印链表信息
    cout<<"请输入要删除结点的学号:";
    cin>>num1;
    deleteNode(num1,n,inc);  //调用删除结点函数并打印链表信息
    cout<<"请输入要查找结点的学号:";
    cin>>num2;
    searchNode(num2,n,inc-1);  //调用查找对应学号结点信息的函数
    cout<<"请输入要修改结点的学号:";
    cin>>num3;
    updateNode(num3,n,inc-1);  //调用更新对应学号结点信息的函数
    return 0;
}

void creatList(int n)
{
    node_head=node_end=new (struct seqList);   //new开辟第一个结点,使头尾指针都指向它
    cout<<"请输入第1个结点中的学号与分数:";
    cin>>node_head->num>>node_head->score;     //录入第0个结点的信息
    for(int i=1;i<n;i++)    //遍历录入第1到n-1个结点的信息
    {
        node_increase=new (struct seqList);   //新增一个结点,使ptr_increase指向这个空间
        node_end->next=node_increase;        //将新增结点与上一个结点链接起来
        cout<<"请输入第"<<i+1<<"个结点中的学号与分数:";
        cin>>node_increase->num>>node_increase->score;
        node_end=node_increase;          //更新尾结点
    }
    printList(n);
}

void printList(int n)
{
    //头尾结点不动,遍历只需用新增结点的指针来挪动
    node_increase=node_head;
    cout<<'\n'<<"****当前链表记录的最新信息如下****";
    cout<<'\n'<<"学号"<<'\t'<<"成绩"<<endl;
    for(int i=0;i<n;i++)
    {
        cout<<node_increase->num<<'\t'<<node_increase->score<<endl;
        node_increase=node_increase->next;      //要了解清楚所写结构,有一个想象空间
    }
    cout<<endl;
}

void increaseNode(int n, int increase_n)
{
    for(int i=n;i<n+increase_n;i++)    //遍历录入新增n个结点的信息
    {
        node_increase=new (struct seqList);   //新增一个结点,使ptr_increase指向这个空间
        node_end->next=node_increase;        //将新增结点与上一个结点链接起来
        cout<<"请输入新增结点中的学号与分数:";
        cin>>node_increase->num>>node_increase->score;
        node_end=node_increase;          //更新尾结点
    }
    //n=n+increase_n;
    cout<<endl;
    printList(n+increase_n);
}

//遍历线性表查找到对应学号的结点就可以了,所以我们需要知道目前线性表的结点个数
void deleteNode(int num,int n,int increase_n)   //删除给定学号的结点
{
    node_increase=node_head;
    for(int i=0;i<n+increase_n;i++)
    {
        if(node_increase->num!=num)   //不是要删除的结点,就继续查找下一个
        {
            //如果下一个结点是要删除的结点,那么下面这一步可以保存删除结点的前一结点的访问方法
            node_end=node_increase;
            node_increase=node_increase->next;
        }
        else
        {
            //这里有一个问题:如果删除的是头结点呢
            if(i==0)    //即说明要删除的是头结点
            {
                node_head=node_increase->next;   //将第二个结点指定为头结点
                //free(node_increase);      //断掉删除结点与前后结点的联系,释放指针
                break;
            }
            else
            {
                //这里需要删除要删除的结点,并把该结点的前一个结点和后一个结点链接起来,所以这里我们还需要用到node_end辅助
                node_end->next=node_increase->next;   //将删除结点的前后结点链接起来
                //free(node_increase);      //断掉删除结点与前后结点的联系,释放指针
                break;
            }
        }
    }
    printList(n+increase_n-1);
}

void searchNode(int num,int n,int increase_n)
{
    node_increase=node_head;
    for(int i=0;i<n+increase_n;i++)
    //这里为什么是i<n+increase_n?
    //因为for循环里我写的是以下的查找逻辑结构,针对尾结点所以需要修改循环条件,否则查不到最后一个结点
    {
        if(node_increase->num==num)   //查找对应学号的结点
        {
            cout<<node_increase->num<<'\t'<<node_increase->score<<endl;
            break;
        }
        else
        {
            if(node_increase->next==NULL)
            {
                cout<<"链表中没有这个学号对应的信息,查找失败。"<<endl;
                break;
            }
            else     //否则继续查找下一个结点
            {
                node_increase=node_increase->next;
            }
        }
    }
}

void updateNode(int num,int n,int increase_n)
{
    node_increase=node_head;
    for(int i=0;i<n+increase_n;i++)
    {
        if(node_increase->num==num)
        {
            cout<<"当前结点的信息如下:"<<endl;
            cout<<node_increase->num<<'\t'<<node_increase->score<<endl;
            cout<<"请输入你要修改的学号与分数:"<<endl;
            cin>>node_increase->num>>node_increase->score;
            cout<<"修改后的结点信息为:"<<endl;
            cout<<node_increase->num<<'\t'<<node_increase->score<<endl;
            printList(n+increase_n);
            break;
        }
        else if(node_increase->next==NULL)
        {
            cout<<"链表中没有这个学号对应的信息,查找失败。"<<endl;
            break;
        }
        else     //否则继续查找下一个结点
        {
            node_increase=node_increase->next;
        }
    }
}

运行结果:


个人总结:

学了C++也有一段时间了,从看网课到一点一点的码一些小代码,到现在回归书本再一次过滤,积累感悟了很多,就想着自己实现一下链表的增删改查。C中指针这一块确实存在较大难度,实现时,前前后后存在很多处理指针不当的问题,历时三天,一个人慢慢修改慢慢调试写的也大概差不多了。

ps:写链表一定要有空间想象能力,熟悉链表结构,能灵活运用链表的三个指针:node_head,node_end,node_increase。难点是如何在操作链表后还可以把结点与结点之间连贯起来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值