基于C语言实现单链表的最基础的增删改查
目录
理论部分:
链表也是一种线性表,它在物理上是分散的,但是在逻辑上是线性的
根据分类单链表大致可分为环状单链表和非环状单链表
这里只讨论最基础的单链表,非环状单链表,
单链表是由一个又一个的节点通过指针连接而成的,而每一个节点又分为数据域和指针域,顾名思义,也就是数据域是存放数据的,而指针域是一个指向结构体类型的指针,它指向后一个节点
根据节点(逻辑上的)位置的不同,节点又可分为:
头结点:头结点一般数据域不用来存放数据,一般也不会删除头结点,所以通常定义一个头指针来指向链表的第一个存放数据的节 点,我一般习惯创建一个结构体来保存链表的属性同时保存链表的头指针
中间节点(普通节点):中间节点的指针域都指向它的下一个节点,数据域保存数据
尾节点:尾节点的指针域指向空
实践部分:
1、单链表的创建
要对单链表进行增删改查的基本操作,首先要先有这个单链表
首先单链表的结构体以及单链表属性的结构体如下
struct Node//链表节点结构体
{
short date;//节点的数据域
Node* next;//指向下一个节点的指针域
};
struct list//单链表的属性结构体
{
Node* head;//指向链表第一个节点的头指针
int size;//记录链表中节点个数的多少
};
创建单链表函数
list* creat_list()
{
list* temp=(list*)malloc(sizeof(list));//先向内存申请一个结构体属性空间
if(temp==NULL)
{
printf("内存不足\n");
exit(0);
}
temp->head=NULL;//因为链表刚刚创建一个节点都没有,所以头指针指向空
temp->size=0;//节点个数为0
return temp;//返回出去链表属性节点
};
2、插入数据(增)
插入数据的原理就是将要插入节点的指针域指向下一个节点,将上一个节点的指针域指向要插入的节点
形象一点就是下面的内存图(自己用画图工具画的,比较难看,但是还能看出来)
链表中数据的插入要进行讨论是在头部插入还是在其他位置插入,从下面的图里面也可以看出在头部插入和在其他位置进行插入是不同的
数据插入函数(指定插入位置)
void insert_Node(list* temp,short date,short n)//参数分别代表,链表的属性结构体,要插入的数据,要插入的位置
{
Node* p=(Node*)malloc(sizeof(Node));
if(p==NULL)
{
printf("内存空间不足\n");
return;
}
//初始化刚申请的节点
p->date=date;//赋值
p->next=NULL;//指针域置空
if(temp->head==NULL)//如果此时链表为空,那么temp->head指向NULL,此时再对temp->head->next是非法的,所以需要判断
{
temp->head=p;
p->next=NULL;
return;
}
else
{
if(n==1)//头插
{
p->next=temp->head->next;//将p的指针域指向原本的第一个节点,p成为现在的第一个节点
temp->head=p;//将头指针重新直线现在的第一个节点
temp->size++;//节点个数加1
return;
}
else//其他位置插入
{
Node* cur=temp->head;//cur是用来遍历链表的一个游标指针
for(short i=1;i<n-1&&cur!=NULL;++i,cur=cur->next);//访问链表,直到第n-1个节点的位置
if(cur==NULL)//如果第n-1个节点不存在,则不能直接插入到第n个位置,插入位置不合法
{
printf("插入位置不合法\n");
return;
}
p->next=cur->next;//将待插入的节点的指针域指向下一个节点
cur->next=p;//将上一个节点的指针域指向待插入的节点
temp->size++;//节点个数加1
}
}
}
2、删除数据 (删)
删除数据的中心思想就是将要删除的节点“摘”下来,然后释放掉,所以要定义一个临时指针变量来保存这个要释放的节点,然后连接这个节点的上下节点,连接完成之后再将这个节点释放掉
就像下面的内存图一样(还是注重内容,请自觉忽略本人的画功……)
根据理论,删除第一个节点和删除其他位置的节点是不一样的
删除函数:作用是删除链表指定数据
删除函数分两种:1、删除指定位置的节点。2、删除指定数据
由于第一中删除函数和上面的插入函数有异曲同工之处,所以这里就用第二种删除函数,删除指定数据
void del_Node(list* temp,short date)//参数分别代表链表属性,要删除的数据
{
if(temp->head==NULL)//如果链表为空,说明链表内没有数据可删除,返回主调函数
{
printf("链表内没有数据.\n");
return;
}
//这里用while循环判断而不用if判断,是因为删除第一个节点之后,第二个节点成为新的第一个节点应
//该在判断一次
//举个例子就明白了,如果链表中数据为1 1 1 2 1 2,要删除的数据时1,那么当删除第一个节点之后新
//链表为:1 1 2 1 2,第一个节点还是1所以还是应该被删除,如果用if判断就将会漏掉这个节点,所以
//用while循环
while(temp->head!=NULL&&temp->head->date==date)//如果当前第一个节点就是应该被删除的节点
{
Node* p=temp->head;//定义临时指针p,指向temp->head所指向的节点
temp->head=temp->head->next;//头结点指向第二个节点,第二个节点成为第一个节点
temp->size--;//节点个数减1
free(p);//释放内存
}
//删除其他位置的节点
Node* cur=temp->head;//定义游标指针,用来遍历链表
while(cur!=NULL)
{
if(cur->next->date==date)
{
Node* p=cur->next;//定义临时指针p指向要删除的数据
cur->next=p->next;//将p的上一个节点和p的下一个节点连接起来
temp->size--;//节点个数减1
}
}
printf("删除完成\n");
}
3、改变数据(改)
数据的更改,也就是将原本的数据改变为新的数据,
更改函数还是有两种,其实都是在上面的插入数据的函数和删除函数的基础上,很简单
1、改变指定位置的数据 2、改变指定数据
现在将两种都讨论一下
1、改变指定位置
void change_date(list* temp,short newdate,short n)//分别代表链表的属性,改变的新值,要进行改变的位置
{
if(temp->head==NULL)
{
printf("链表中没有数据\n");
return;
}
Node* p=temp->head;
for(short i=1;i<n&&p!=NULL;++i,p=p->next);//游标指针偏移到第n个位置,
if(p==NULL)//如果p=NULL,就说明游标指针偏移出链表,说明插入位置不合法
{
printf("删除位置不合法\n");
return;
}
p->date=newdate;//将第n个位置上的数据域保存的数据改变
}
2、改变指定数据
void change_date(list* temp,short date,short newdate)//分别代表链表的属性,要改变的数据,改变之后的数据
{
if(temp->head==NULL)//判断链表中有没有数据
{
printf("链表中没有数据\n");
return;
}
Node* p=temp->head;//定义一个游标指针,
while(p!=NULL)
{
if(p->date==date)
p->date=newdate;
p=p->next;
}
}
4、数据查找
数据的查找很简单,
也有两种:1、查找指定数据的位置 2、查找指定位置上的数据
1、查找指定数据
void search_date(list* temp,short date)
{
if(temp->head==NULL)
{
printf("链表中没有数据\n");
return;
}
Node* p=temp->head;//定义一个“游标”指针用来遍历链表
int n=0,postion=1;//n记录date在链表中出现的次数,postion记录date在链表中出现的位置
while(p!=NULL)
{
if(p->date==date)//如果找到数据date
{
printf("%d在链表中的%d个位置上\n",date,postion);//输出给数据的位置
n++;//该数据的个数加1
}
p=p->next;//指针偏移
postion++;//位置加1
}
printf("%d在链表中一共出现了%d次\n",date,n);
}
2、查找指定位置,也就是将游标指针偏移到指定位置
void serach_date(list* temp,short n)//查找指定位置的数据,n为要查找的位置
{
if(temp->head==NULL)//判断链表中是否有数据
{
printf("链表中没有数据\n");
return;
}
if(n<1||n>temp->size)//增强程序的健壮性//很显然链表最少1个链表最大temp->size个数据,查找位置超
//出就说明,要查找的位置不合法
{
printf("查找位置不合法\n");
return;
}
Node* p=temp->head;//定义一个游标指针
for(short i=1;i<n;++i,p=p->next);
//游标指针的偏移,通过上面的if判断查找位置的合法性,显然这里的p是不可能等于NULL的,所以下面的数据可以直接输出
printf("第%d"个位置上的数据为:%d\n",n,p->date);
}
5、扩展部分
最后的扩展提升部分,上面的都是链表的常规操作,链表还有很多操作,
比如:1、链表的排序
2、将两个有序的链表连接成一个有序的链表,
3、判断一个单链表是否是环状链表
4、单链表的逆序
等等……
最后
今天的总结就到这里,希望对能读者起到一点帮助,
让我们共同进步