C语言链表详解:为什么需要链表及链表的基本用法与定义
C语言中链表有什么用?

链表是更基本、更重要的数据结构类型之一。那么既然有了数组,为什么还需要链表呢?或者说设计链表这样的数据结构的初衷是什么?下面小编就给大家介绍一下C语言中链表的使用。
链表的定义:
链表的定义一般使用结构体。在看《数据结构与算法分析》这本书时,发现书中频繁使用typedef关键字。结果真的很棒。它不仅可以保持代码的干净,还可以让我们在接下来的编码过程中,消除很多烦人的指针(当然,指针仍然存在)。所以这里也借用了书上的定义方法。
结构节点;
typedef 结构节点* PtrNode;
typedef PtrNode 位置;
typedef PtrNode 列表;
结构节点{
整数值;
PtrNode 下一个;
};
接下来编写一个创建链表的函数,输入每个节点的值,当值为-1时函数结束。
在此,我一直不明白为什么需要定义三个Node*。现在我终于明白了。终于复习了指点的内容,明白了。这里我就讲一下指针的实现。链表对指针的操作非常频繁,需要更加可靠。掌握了指针之后,看链表就会容易很多。在下面的程序中,我定义了三个指向节点结构的指针:分别是head/p/tmp。团长的主要作用就像传销头子一样。他会主动去联系一个离线的p,然后他就不干了,p又接着开发一个又一个离线的tmp,结果一系列以head为首的链表就出来了。
一开始我总觉得有了head,为什么还需要p?这是因为如果我们直接用head来指向下一个节点的话,head的位置是在不断移动的,也就是永远在链表的末尾,所以当我们回到链表时,它实际上是一个空值。因此,我们需要p作为中转链路。 (其实这种做法在指针中很常见,大多数返回指针类型的函数都会先定义一个指针变量来保存函数传入的参数,而不是直接对参数进行操作)。
/*
功能:创建一个链表
功能说明:每输入一个新的整数,就会增加一个新的节点来存储该整数。
当输入整数为-1时,函数结束。
*/
列表创建()
整数n=0;
位置 p,head,tmp;
头=空;
tmp=malloc(sizeof(struct Node));
如果(tmp==NULL)
printf("tmp malloc 失败!");
返回空值;
别的
p=tmp;
printf("请输入第一个节点的消息!");
scanf("%d",&(tmp->值));
while(tmp->值!=-1)
n+=1;
如果(n==1)
头=p;
tmp->下一个=NULL;
别的
p->下一个=tmp;
p=tmp;
tmp=malloc(sizeof(struct Node));
printf("请输入第%d个节点!",n+1);
scanf("%d",&(tmp->值));
p->下一个=NULL;
免费(tmp); //free函数只释放申请的空间,但指针仍然存在。
tmp=空;
返回头;
接下来编写一个删除链表节点的函数,输入一个整数,然后遍历链表节点。当链表节点的值等于整数时,该节点被删除。
在完成这个功能之前,首先要想清楚流程。不可否认,我是一个一上来就敲代码的人。读完《剑指Offer》后,我觉得这个习惯是程序员的大忌,甚至想写一篇文章。我已经给我的博客想好名字了,《程序员的修养:思考第一,代码第二》。其实仔细想想,写程序的目的是为了解决问题,而不是单纯的写程序。单纯让程序跑起来,恐怕只有学校里才有吧!在真正的程序开发中,几乎所有能想到的实际问题都需要考虑,所以无论写多少程序,在写程序之前首先要学会思考清楚。
对于这个功能,我们要想到的是:
1 如果链表为空怎么办?当然是直接返回。
2 如果要删除的元素是头节点怎么办?
3 如果要删除的元素是尾节点怎么办?
当我们注意以上三部分的时候,我们的程序就可以避免输入链表为空而程序直接崩溃的现象,也可以避免当元素值是头节点时无法删除的尴尬。 。我们的程序具有一定的鲁棒性。
下面重点介绍链表删除的实现:
列表: ???? Node_a->Node_b->Node_c->Node_d;
列表:?????? Node_a->Node_b------------>Node_d
假设我们要删除的节点是上图中的Node_c;假设我们可以找到Node_c的前一个位置tmp和被删除节点的位置p;这时我们只需要执行tmp->Next=p->Next即可。
只要我们完成了上面的分析,考虑到了各种情况,那么我们完成下面的代码就水到渠成了。
/*
功能:删除链表中指定值的节点(如果有多个,只删除第一个)
本例中,输入一个整数,并从链表中删除值为该整数的节点。
*/
列表删除节点(List列表)
位置 p,tmp;
整数值;
如果(列表==NULL)
printf("列表为空,函数返回!");
返回空值;
别的
printf("请输入节点的值:");
scanf("%d",&值);
p=列表;
if(p->值==值)
列表=p->下一个;
免费(p);
p=空;
返回列表;
while(p!=NULL&&p->值!=值)
tmp=p;
p=p->下一个;
if(p->值==值)
if(p->下一个!=NULL){
tmp->下一步=p->下一步;
别的
tmp->下一个=NULL;
免费(p);
p=空;
返回列表;
链表的使用场景分析:
链表在程序开发中的使用还是非常频繁的,所以链表往往是用高级语言来实现的,比如STL中的列表以及Java中类似的东西。目前服务器端开发中,链表主要用于接收从数据中取出的一些数据进行处理。
即使你不知道链表的底层实现,你仍然可以成功地使用STL中现成的东西。但作为一个学习者,我觉得能够使用和从底层掌握它还是两个不同的概念。 Linux之父说:“少说话,给你看代码”。
下面的程序使用链表来模拟手机通讯录的功能,包括添加联系人、查找联系人、删除联系人等。
PS:关于健壮性,程序中最大的危险是gets函数的使用。目前就继续使用gets,等找到工作后程序进一步完善。 (尼玛,去读书吧……刚毕业的,工作怎么这么难找啊??工作经历,工作经历,操,那大佬一出校门就什么都知道了。)
/******************************************************** ***** *************************
节目:
这是由列表编写的电话列表
该程序对于列表来说只是昂贵的
作者:热南
邮箱:964465194@qq.com
数据:2015/07/27
****************************************************** * **********************/
#包括
#包括
#包括
#定义N 25
#定义M 15
结构节点;
typedef 结构节点* p_node;
typedef p_node 列表;
typedef p_node 位置;
typedef 结构节点** PList;
结构节点{
字符名称[N];
字符数[M];
下一个位置;
};
int JudgeNameExist(List 列表,char* name);
无效AddPerson(PList列表);
无效PrintList(列表列表);
列表FindPerson(列表列表);
列表 FindPersonByName(列表列表,char* 名称);
int AddPersonByName(PList列表,列表节点);
int DeletePersonByName(PList 列表,char* 名称);
无效DeletePerson(PList列表);
int main()
列表列表=NULL;
位置p;
字符命令[100];
同时(1)
printf("主");
printf("************ 1 添加一个人********");
printf("************ 2 显示手机列表********");
printf("************ 3 从手机列表中查找********");
printf("************ 电话列表中的 4 ********");
printf("请输入命令号:");
获取(cmd);
开关(命令[0])
情况'1':
添加人员(&列表);
休息;
情况“2”:
打印列表(列表);
休息;
情况“3”:
查找人员(列表);
休息;
情况“4”:
删除人(&列表);
休息;
默认:
printf("命令错误!");
休息;
返回0;
/*
功能:判断电话本中是否已存在要添加的联系人姓名。
输入:List Phone list, name 要添加的联系人姓名。
返回:存在则返回1,不存在则返回0。
*/
int JudgeNameExist(列表列表,char*名称)
if(FindPersonByName(列表,姓名)!=NULL)
返回1;
别的
返回0;
/*
功能:根据输入的姓名查找联系人的信息节点
输入:要输入的电话号码列表、姓名
返回:返回找到的节点
*/
列表 FindPersonByName(列表列表,char* 名称)
while(列表!=NULL)
if(strcmp(列表->名称,名称)==0)
休息;
列表=列表->下一个;
返回列表;
/*
功能:根据姓名添加新联系人到联系人列表
输入:指向联系人列表地址的指针,新用户节点
返回:加法成功返回1,加法失败返回0。
*/
int AddPersonByName(PList列表,列表节点)
if(节点==NULL)
printf("节点为NULL!");
返回0;
if(*列表==NULL)
*列表=节点;
返回1;
列表 pHead=*列表;
while(pHead->下一个!=NULL)
pHead=pHead->下一个;
pHead->下一个=节点;
返回1;
无效AddPerson(PList列表)
位置tmp;
位置p_head;
tmp=(结构节点*)malloc(sizeof(结构节点));
字符名称[N];
字符数[M];
如果(tmp==NULL)
printf("malloc tmp 节点在 add person 函数中失败!");
别的
printf("请输入姓名:");
获取(名称);
printf("请输入数字:");
获取(数字);
strcpy(tmp->名称,名称);
strcpy(tmp->数字,数字);
tmp->下一个=NULL;
if(JudgeNameExist(*列表,名称)==1)
免费(tmp);
printf("该名称已经存在!");
返回;
AddPersonByName(列表,tmp);
/*
功能:打印联系人列表
输入:联系人列表
*/
无效打印列表(列表列表)
位置显示;
显示=列表;
如果(显示==NULL)
返回 ;
printf("现在,我们打印电话列表:");
while(显示!=NULL)
printf("姓名:%s 号码:%s",show->姓名,show->号码);
显示=显示->下一个;
列表FindPerson(列表列表)
字符名称[N];
位置 pHead=列表;
printf("请输入您要查找的名称:");
获取(名称);
位置节点=FindPersonByName(列表,姓名);
if(节点!=NULL)
printf("查找成功!名称->%s编号->%s",节点->名称,节点->编号);
别的
printf("查找失败!");
返回节点;
/*
功能:根据姓名删除联系人
输入:指向联系人地址、联系人姓名的指针
输出:删除成功返回1,失败返回0。
*/
int DeletePersonByName(PList 列表,char* 名称)
if(*列表==NULL||名称==NULL)
返回0;
列表 pHead=*列表;
if(strcmp(pHead->名称,名称)==0)
*列表=pHead->下一个;
自由(pHead);
pHead->下一个==NULL;
返回0;
列表 tmp=pHead->下一个;
而(tmp!= NULL)
if(strcmp(tmp->名称,名称)==0)
pHead->下一个=tmp->下一个;
免费(tmp);
tmp->下一个=NULL;
返回1;
pHead=tmp;
tmp=tmp->下一个;
返回0;
无效DeletePerson(PList列表)
列表 pHead=*列表;
如果(pHead==NULL)
printf("没有可以删除的人");
返回 ;
字符名称[N];
printf("请输入姓名:");
获取(名称);
DeletePersonByName(列表,姓名);