链表是除数组外的另一种保存数据的方式,用到了结构、函数、指针等知识,叫链表纯粹是因为结构体变量通过指针连接起来形象的叫做链表。
在建立一个链表前,我们首先要建立一个结构体,包括数据域和指针域两个部分的结构体变量就是结点。如
typedef struct student {
int num;
char name[20];
struct student *next; //指针
};student
通过以上结构体我们建立一个结点:
student node1;
可以看到,所谓结点,其实就是结构体变量,以此类推我们可以建立n个节点并初始化:
student node1={1,NULL};
student node2={2,NULL};
student node3={3,NULL};
建立好了结点,接下来需要把结点连起来形成一个链表,使用指针指向地址的原理,第一个结点的next指针指向第二个结点的地址,以此类推。其中定义首地址head,尾地址指向空。如下
student *head; //定义一个结构指针head
head=&node1; //head指针指向node1的地址,表示链表头
node1.next->&node2; //node1的next指向node2地址
node2.next->&node3;//node2的next指向node3地址
node3.next->NULL;//node3指向空,表示链表结束
这样一个链表就定义完毕,接下来可以对链表的数据域进行操作。
上面定义的是静态链表,如果要定义动态链表就需要用到malloc来分配空间:
student * node1;//d定义一个结构指针作为结点
node1=(student *)malloc(sizeof(student)); //分配地址
/*也可以直接写成:
student * node1=(student *)malloc(sizeof(student));
动态链表各结点的链接与静态链表相同。
在建立一个链表时,需要定义多个结构指针,包括头指针head,不变,永远指向第一个结点,分配空间指针,用于创立新结点;以及辅助指针,用于链表信息的临时保存等,辅助链表延伸。
链表最后一个结点的next指针必须为空。
新建及添加链表步骤为:
1.定义三个结构指针,head,p,st
2.给st分配一个空间,建立第一个结点,并令head指向st
3.给第一个结点赋值,其中st->next=NULL
4.令p指向st,给st分配一个空间,p->next=st,
st->next=NULL
5.给新结点赋值
6.重复4,5直到循环结束
printf("输入要添加几个人:");
scanf("%d",&a);
scanf("%d%s",&num,name);
student *head,*st,*p;//工具人st、p指针,拓展结点用:p先指向st,st分配新结点,
//然后p后移一位指向新st,主要是因为st->next需要为空,因此p指针过度
st=(student *)malloc(sizeof(student)); //插入第一个结点
p=head=st;
st->num =num;
strcpy(st->name,name);
st->next =NULL;
b=a-1;
while(b--) //添加新结点
{
scanf("%d%s",&num,name);
st= add(head,st);
st->num =num;
strcpy(st->name,name);
}
st->next =NULL;
for(st=head;st!=NULL;st=st->next ) //遍历
{
printf("%d %s\n",st->num ,st->name );
}
删除链表结点
基本思路,先输入一个数,然后遍历链表,寻找与输入的数相等的结点,然后先将此节点的前后结点连接,再释放该节点空间。
可以知道,删除一个结点最多需要用到三个结点,因此需要引入两个结构指针赋值,起名为p_front,和p_behand。
其中我们要删除的结点是p_behand,该节点的前一个结点是p_front,该节点后一个结点的地址是p_behand->next。p_front用来保存删除结点前一个结点信息,因此若满足,p_front不动,不满足条件p_front后移;p_behand始终要保证p_behand是p_front的下一个结点
而前、后两个辅助指针通过head头结点来完成初定义:
p_front=head;
p_behand=head->next;
这样就定义好了辅助指针。
**还需要注意一个点,**如果我们要删除的恰好是头结点,那么我们需要这样操作:
p_behand=head; //把头结点的地址赋给辅助结点
head=head->next; //原头结点的下一个结点地址作为新头结点地址
free(p_behand); //删除原头结点
下面是代码:
scanf("%d",&c);
/*删除结点为头结点*/
while(head!=NULL&&head->num ==c)
{
p_behand=head;
head=head->next ;
free(p_behand);
}
/* 删除结点不为头结点*/
p_front=head;
p_behand=head->next ;
while(p_behand!=NULL)
{
if(c==p_behand->num ) //满足条件
{
p_front->next =p_behand->next;//连接结点
free(p_behand); //释放空间
}
else //不满足条件
p_front=p_behand; //辅助结点front后移
p_behand=p_front->next ;
}
链表信息的修改
修改就很简单了,只需要先从头遍历链表,然后用条件语句找到要修改的值,直接修改即可:
printf("输入要修改的人的学号");
scanf("%d",&d);
for(st=head;st!=NULL;st=st->next )
{
if(st->num ==d)
{
printf("输入要修改姓名:");
scanf("%s",s);
strcpy(st->name ,s); //修改姓名
}
}
查看链表
若要显示所有信息,只需从头遍历。若要设置条件显示,如显示学号1的人信息,同样先遍历,再用条件语句控制输出即可:
printf("输入要显示的学号");
scanf("%d",&a);
for(st=head;st!=NULL;st=st->next )
{
if(st->num ==a)
printf("%d %s\n\n",st->num ,st->name );
}
现在链表的建立、遍历以及用链表实现增删查改的功能就介绍完毕,其中修改和查看较为简单,不需引入辅助指针;添加需引入两个辅助指针,删除需引入两个辅助指针。需要多思考,理解链表的原理就是用指针和malloc、free函数对数据的存储空间进行操作。除了head头结点特殊不可随意改动外,其他的指针都可以叫做辅助指针。辅助指针可以形象比喻成织布的“针”,织布的时候需要针,布织好了,针也就可以拿走了。也可以叫工具人。
其实计算机内部处理数据,并没有结构、数组等概念,这些概念是我们人为规定的。而计算机中只有地址和地址中的数据的概念。很多初学c语言的人觉得链表难,其实很大原因就是不理解计算机中的数据是如何存储的。因此想学好链表,学好c语言,最好了解计算机的工作原理也就是微机原理,80X86型号就足矣。
最后再附上一段最基本的链表代码,其实大部分上面已经写了,这里给出一个能完成运行的代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct student{
int num;
char name[20];
struct student *next;
}student;
void main()
{
int a,b,c,d,i;
int num;
char name[20];
char s[20];
printf("输入要加入的人数:");
scanf("%d",&a);
scanf("%d%s",&num,name);
student *head;
student *st,*p;//工具人st、p指针,拓展结点用:p先指向st,st分配新结点,
//然后p后移一位指向新st,主要是因为st->next需要为空,因此p指针过度
/*建立链表*/
st=(student *)malloc(sizeof(student));
p=head=st;
st->num =num;
strcpy(st->name,name);
st->next =NULL;
/*增加*/
b=a-1;
while(b--)
{
scanf("%d%s",&num,name);
st=(student *)malloc(sizeof(student));//新建结点
st->num =num;
strcpy(st->name,name);
p->next =st;
st->next=NULL ;
p=st;
}
/*删除*/
printf("输入要删除的学号:");
scanf("%d",&c);
while(head!=NULL&&head->num ==c)//删除结点为头结点
{
p=head;
head=head->next ;
free(p);
}
p=head; //删除结点不为头结点
st=head->next ;
while(st!=NULL)
{
if(c==st->num )
{
p->next =st->next;
free(st);
}
else
p=st;
st=p->next ;
}
/*修改*/
printf("输入要修改的人的学号");
scanf("%d",&d);
for(st=head;st!=NULL;st=st->next )
{
if(st->num ==d)
{
printf("输入要修改姓名:");
scanf("%s",s);
strcpy(st->name ,s);
}
}
/*显示*/
printf("输入要显示的学号");
scanf("%d",&a);
for(st=head;st!=NULL;st=st->next )
{
if(st->num ==a)
printf("%d %s\n\n",st->num ,st->name );
}
/*遍历*/
for(st=head;st!=NULL;st=st->next )
{
printf("%d %s\n",st->num ,st->name );
}
}
这里代码并没有写成函数形式,如果写成函数,要返回head指针。