第九章 使用结构体类型处理组合类型(链表)
1、单链表
线性链表就是动态地进行存储分配的一种数据结构,图为单向链表结构:

- 链表有一个“头指针 ”变量(head),它存放一个地址,该地址指向链表中的一个元素;
- 链表中的各元素称为“结点”,每个结点包含两个部分:数据部分 和一个指针变量 (下一结点的起始地址);
- 如果一个元素存放的地址为“NULL"(表示“空地址”),那么成元素称为“表尾”
- 链表中各结点在内存中的地址可以是不连续的,但是必须要有“头指针”(head),则整个链表都无法访问。
- 链表是利用结构体变量 和 指针变量 实现的。
struct student
{
int num;
float score;
struct student *next; // next成员的指针变量
};
分析:next是指针类型的成员,next定义为指向struct student类型数据的指针变量。next既是struct student 类型的成员,又是struct student类型的数据。
2、建立静态链表
#include <stdio.h>
#define NULL 0 // 表尾指向NULL
struct student // 声明结构体类型struct student
{
int num;
float score;
struct student *next;
};
int main()
{
struct student a,b,c,*head,*p; // 定义3个结构体变量作为链表的结点和两个指针变量
a.num=10101;a.score=89.5; // 对各个结点成员赋值
b.num=10103;b.score=78;
c.num=10105;c.score=90.5;
head=&a; // 头指针指向a结点
a.next=&b; // a的next成员指向b结点
b.next=&c; // b的next成员指向c结点
c.next=NULL; // c的next成员指向NULL
p=head; // p也指向head p指向head,head指向a,即p指向a
do
{
printf("%ld %5.1lf\n",p->num,p->score);
p=p->next; // 初次看不懂就看成 (*p).next吧!如果不加这个将无限循环
}while(p!=NULL);
return 0;
}
思考: ① 各个结点是怎样构成链表的? 必须要有head指针作为链表入口,必须要有指向下一个结点的指针p,必须要有结尾NULL
② 没有头指针head行不行? 不行,没有head,那么就第一个p就为空,无法访问链表
③ p起什么作用?没有它行不行? p是指向下一个结点的作用,没有它不行啊!
PS:静态链表不行临时开辟的,用完后不能释放。
3、建立动态链表
例子1:建立一个有两名学生学号和成绩数据的单向动态链表
思路:
1 #include <stdio.h>
2 #include <malloc.h> // malloc函数开辟新单元时需要用此头文件
3 #define LEN sizeof(struct student) // LEN代表struct student 类型数据所需的字节数
4 struct student
5 {
6 int num;
7 float score;
8 struct student *next; // 指针变量成员
9 };
10
11 int main()
12 {
13 struct student *head,*p; // 定义指针变量head和p
14 head=p=(struct student*)malloc(LEN); // 开辟1个新单元,并让p和head指向它 指向新开辟的内存地址,head和p一开始是相等,即指向第1个结点的地址
15 scanf("%d,%f",&p->num,&p->score); // 输入第1个结点的数据
16 p=(struct student*)malloc(LEN); // 开辟第2个新单元,并让p指向它 此时p为指向第2个结点的地址,但是head没有变
17 scanf("%d,%f",&p->num,&p->score); // 输入第2个结点的数据
18 head->next=p; // 使第1个结点中的next成员指向第2个结点 head没有改变,p改变了,head->next第一个结点的next成员,所以赋值为了p(即第2个结点地址)
19 p->next=NULL; // 使第2个结点中的next成员不指向任何对象 此时p为指向第2个结点的地址
20
21 p=head; // p本来是指向第2个结点,现在p赋值为头指针地址,即第1个结点地址
22 printf("\n结点1:%d,%6.2f\n",p->num,p->score); // 输出第1个结点中的数据
23 p=p->next; // p赋值为第1个结点的next成员的值(即第2个结点地址)
24 printf("\n结点1:%d,%6.2f\n",p->num,p->score); // 输出第2个结点中的数据
25 return 0;
26 }

浙公网安备 33010602011771号