参考清华大学软件学院 谌卫军 《c语言程序设计》课件
1、链表
1.1 链表的基本概念
链表中的每一个元素称为一个“结点”,每个结点都应该包括两部分:一为用户需要使用的实际数据;二为下一个结点的起始地址。另外,链表还有一个头指针head,指向链表的首结点。
定义如下的结构体类型
struct Train_tag
{
char Num[8];
char Name[10];
int Weight;
char From[20];
char To[20]; // 数据域
struct Train_tag *next; // 指针域
}
2、对链表的操作
2.1、创建动态链表
例:创建一个链表,并输入每一个结点的各种描述信息(集装箱编号、货物名称、货物重量、发货地点、到货时间等),
直到用户输入的货物重量等于0,表示链表结束。
struct Train_tag
{
char Num[8]; //集装箱编号
char Name[10];//货物名称
int Weight; //货物重量
char From[20];//发货地点
char To[20]; //到货地点
struct Train_tag *next;//指向下一结点
};
struct Train_tag *head,*p,*q;
创建链表的过程可归纳为如下三个步骤:
①只要用户输入的Weight不为0,就要构建链表。基本思路是将一个一个的结点添加至链表中。首先用指针p来申请一个结构体变量的内存空间,并且装入用户输入的各种描述信息,然后将指针q和head都指向它,如下图:
②后续结点的创建:如果用户输入的Weight又不为0,就要构建链表的第二个结点。首先用指针p来申请一个结构体变量的内存空间,并且装入用户输入的各种描述信息,然后要执行q->next=p,把第一个结点的next指针去指向它,从而建立两个结点之间的链接关系。最后再把q指向新的结点。如下图:
第三个结点加入链表的过程:
③链表创建过程的结束:如果用户输入的Weight等于0,意味着链表创建过程的结束,此时指针q所指向的就是链表的最后一个结点,所以要把该结点的next指针赋值为NULL,即执行q->next=NULL,表示这里已是链尾,后面不会再连结点。如下图:
void Create()
{
int Weight;
head = p = q = NULL;
while(1)
{
printf("输入货物重量:");
scanf("%d",&Weight);
if(Weight <= 0) break;
p = malloc(sizeof(struct Train_tag));
p->Weight = Weight;
//输入该结点的其它信息;
if(head == NULL) head = p;//新建的是首结点
else q->next = p; // 不是首结点
q = p; // q指向当前尾结点
}
if(head != NULL) q->next = NULL;
}
2.2、访问链表
在创建好一个链表之后,可以依次地访问该链表当中的各个结点的数据。
void Display(struct Train_tag *head)
{
struct Train_tag *p;
p = head;
while(p != NULL)
{
printf("%s,%s,%d,%s,%s\n",p->Num;p->Name,p->Weight,p->From,p->To);
p = p->next;
}
}
2.3、删除链表结点
情形一、待删除的是首结点
情形二、待删除的不是首结点
void Delete(struct Train_tag *head)
{
struct Train_tag *p,*q;
if(head == NULL) {printf("空链表");return;}
p = head;
while((p!=NULL) && strcmp(p->To,"湖南"))
{
q = p;
p = p->next;//把指针p往后移动一个结点
}
if((p != NULL) && !strcmp(p->To,"湖南"))
{
if(p == head)
head = p->next;//删除的是首结点
else
q->next = p->next;//删除的是中间结点
}
}
2.4、插入链表结点
原则:
①插入操作不应破坏原有链接关系;
②需要插入的这个结点应该把它放在合适的位置上,即有一个插入位置的查找过程。
定义:
struct Train_tag *head;//头指针
struct Train_tag *p; //链表当前结点
struct Train_tag *q; //链表上一结点
struct Train_tag *pNode;//待插入的结点
第一种情况:链表为空,即head=NULL
待插入的pNode结点就是链表中的第一个结点,head = pNode;
第二种情况:若pNode->Weight<=head->Weight,则
pNode->next = head;
head = pNode;
第三种情况:pNode结点插入在链表的中间,设指针q和指针p分别指向相邻的两个结点,q在前p在后。
首先让q=head,让p=head->next,然后让它们顺序往后移动,每次移动一个结点。当满足q->Weight<pNode->Weight<=p->Weight时,pNode就插在q与p之间。
//移动指针
q = p;
p = p->next;
//插入结点
pNode->next = p;
q->next = pNode;
void Insert(struct Train_tag *pNode)
{
struct Train_tag *p,*q;
// 第一种情形,链表为空
if(head == NULL)
{
head = pNode;
return;
}
// 第二种情形,新结点的Weight小于等于首结点
if(pNode->Weight <= head->Weight)
{
pNode->next = head;
head = pNode;
return;
}
// 第三种情形,循环地查找正确的插入位置
q = head;
p = head->next;
while(p != NULL)
{
if(pNode->Weight <= p->Weight)
break;
else
{
q = p;
p = p->next;
}
}
// 将pNode结点插入在正确的位置(q和p之间)
pNode->next = p;
q->next = pNode;
}
2.5、链表的释放
void Destroy(struct Train_tag *head)
{
struct Train_tag *p,*q;
p = head;
while(p != NULL)
{
q = p;
p = p->next;
free(q);
}
}
// head = NULL;
3、环形链表
定义一个名为STUDENT的结构体类型
struct STUDENT
{
int number; //表示同学的编号
struct STUDENT *next;//指向下一位同学
}
struct STUDENT *head,*tail,*p,*prev;
int n,m,i,j;
模块1:输入学生的个数n和不吉利的数字m,然后验证它们的有效性;
模块2:创建一个环形链表,模拟众同学围成一圈的情形;
模块3:进入循环淘汰环节,模拟从1到m报数,让n-1个同学逐一退出圈子的过程;
模块4:输出结果。