链表的基本功能实现
在前面的顺序表中,表结构简单,可以存取表中任一位置元素。但插入删除时需要移动大量元素。原因是顺序表中的数据元素逻辑上相邻,物理位置上也相邻。线性表的另一种链式存储结构—链表,它不要求逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序表的弱点,但也失去了顺序表的可随机存取的优点。顺序表可以随便访问任何一个元素,只需要进行表地址和索引地址相加即可访问该元素。而链表必须从第一个开始一个一个遍历找到。
-
链表的结点中包含了指针域和数据域,由于每个结点只包含一个指针域,故又称为线性链表或单链表。
-
单链表的存取必须从头指针开始进行,最后一个数据元素没有后继,指针为空。数据元素之间的逻辑关系由结点中的指针指示。
-
有时我们会在单链表的第一个结点前附设一个头结点。头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等类的附加信息;头结点的指针域存储指向第一个结点的指针(即第一个元素结点的存储位置)。单链表的头指针指向头结点,若线性表为空表,则头结点的指针域为空。
-
下面是单链表的实现实例:
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。难点是如何在操作链表后还可以把结点与结点之间连贯起来。