一、实验目的
1.掌握链表的基本结构和关系。
2.掌握C指针和结构体的使用方法,掌握链表编程。
链表知识点讲解:
链表的遍历:
有·两种链表,第一种是第一个节点数据部分为空。另一种是第一个节点就存放数据。
两种方式的编程是有点不同的:
第一种方式,不管怎样对链表进行增加修改和删除,head的位置都不会变化,head的值只需要传入到函数中,不需要返回,不需要返回。
第二种方式,如果数据插入到第一个节点,head存储的地址会发生变化。这种方式的链表在定义函数时,需要返回链表的head。
实现思路:
①用head指针永远指向链表头。
②用q指针一开始指向第一个有数据的节点,然后就可以访问到节点中的数据了。访问结束后,q移动到下一个节点(q=q->next;),直到q的值为NULL,结束对链表的访问。
/* 定义学生结构体的数据结构 */
typedef struct Student{
char studentNo[20];
char studentName[11];
}st;
/* 定义每条记录或节点的数据结构 */
typedef struct node{
struct Student data; //数据段
struct node *next; //指针域
}Node,*Link; //Node为node类型的别名,Link为node类型的指针别名
Node st1;
Link p;(Link 本身就是一个指针类型的结构体变量)
链表的插入(建立):
在这个例子中,学生节点的插入是有顺序的(链表按照学号排序),也就是说,每次准备插入一个学生节点时,先要找到这个节点应该插入到那个节点的前面。有几种情况需要考虑:
1.当前链表是空的(head的指针域为空),状态如下(node为新节点(用malloc创立),准备插入的第一个节点):
此时,则直接用head->next = node;这条语句,就可以把node插入到链表中。
if(head->next==NULL)
head->next=node;
2.当前链表已经有节点的情况,状态如下图(node为新建立,准备插入的节点):
想要插入节点就需要两个一前一后的指针
那么此时,就需要比较“node中studentNo”和“p的studentNo”,查看大小关系。如果“node中的studentNo”小于“p中的studentNo”,这把node插入到q,p之间。如果q,p永远保持一前一后的关系,这样才能保证在找到节点位置后,可以顺利的插入。 q=head;
p=head->next;
/* 增加学生记录 */
bool addNode(Link head)
{
Link p,q; //p,q两个节点一前一后
Link node; //node指针指向新创建的节点
node=(Link)malloc(sizeof(Node));
inputStudent(node);
q=head;
p=head->next; //p指向head后面的第一个有效节点
if(head->next==NULL)
head->next=node;
else{
//循环访问链表中的所有节点
while(p!=NULL)
{
if(node->data.studentNo < p->data.studentNo){
//如果node节点的学号比p节点的学号小,则插在p的前面,完成插入后,提前退出子进程
q->next = node;
node->next=p;
return true;
}
else{
//如果node节点的学号比p节点的学号大,继续向后移动指针(依然保持q p一前一后)
q=p;
p=p->next;
}
}
//如果没能提前退出循环,则说明之前没有插入,那么当前node节点的学号是最大值,此时插在链表的最后面
q->next=node;
}
return true;
}
实现步骤:
①
②
③
④
链表的删除:
删除链表的思路:
首先,利用p指针扫描链表,根据学生的学号找到要删除的节点位置。
如果找到,则用以下语句就可以删除p所指向的节点(free p):
q->next = p->next;
free(p);
这里编程的要点是:保证q在前,p在后的关系,这样才能保证在找到节点位置后,可以顺利的删除。
如果当前p的学号不等于要删除的学号,则p指针后移。
要考虑如果找不到学生学号的情况。
测试数据时,应该实验可以找到学号的情形,和给定的学号找不到的情形,然后查看删除是否成功。
链表的修改,链表的查询:
这两个功能和删除链表的思路类似:
都是先找到要处理的节点,然后处理即可。
“修改”还需要考虑修改内容是否是学号,如果修改了学号,则应该编写程序保证不能破坏链表有序性的这个特点。
链表的清除:(养成好的习惯)
当在菜单中选择了退出功能后,则结束程序。在结束程序之间,要考虑到一个问题:前面用malloc语句建立的节点,在main函数退出时,分配的存储空间是不会自动释放的,所以必须主动释放链表中所有节点占用的内存。(free语句释放所有malloc产生的节点)
定义一个clearLink函数,实现链表的遍历,并用free语句释放所有malloc产生的节点。
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdbool.h>
/* 定义学生结构体的数据结构 */
typedef struct Student{
char studentNo[20];
char studentName[11];
}st;
/* 定义每条记录或节点的数据结构 */
typedef struct node{
struct Student data; //数据段
struct node *next; //指针域
}Node,*Link; //Node为node类型的别名,Link为node类型的指针别名
//定义提示菜单
void myMenu()
{
printf("******************************菜 单*************************************\n");
printf(" 1 增加学生记录 2 删除学生记录 \n");
printf(" 3 查找学生记录 4 修改学生记录 \n");
printf(" 5 统计学生人数 6 显示学生记录 \n");
printf(" 7 退出系统 \n");
printf("***************************************************************************\n");
}
void inputStudent(Link l)
{
printf("请输入学生学号:");
scanf("%s",l->data.studentNo);
printf("请输入学生的姓名:");
scanf("%s",l->data.studentName);
//每个新创建的节点的next指针域都初始化为NULL
l->next = NULL;
}
void inputStudentNo(char s[],char no[])
{
printf("请输入要%s的学生学号:",s);
scanf("%s",no);
}
void displayNode(Link head)
{
//填写代码,根据传入的链表head头指针,扫描链表显示所有节点的信息
}
bool addNode(Link head)
{
Link p,q; //p,q两个节点一前一后
Link node; //node指针指向新创建的节点
node=(Link)malloc(sizeof(Node));
inputStudent(node);
q=head;
p=head->next; //p指向head后面的第一个有效节点
if(head->next==NULL)
head->next=node;
else{
//循环访问链表中的所有节点
while(p!=NULL)
{
if(node->data.studentNo < p->data.studentNo){
//如果node节点的学号比p节点的学号小,则插在p的前面,完成插入后,提前退出子进程
q->next = node;
node->next=p;
return true;
}
else{
//如果node节点的学号比p节点的学号大,继续向后移动指针(依然保持q p一前一后)
q=p;
p=p->next;
}
}
//如果没能提前退出循环,则说明之前没有插入,那么当前node节点的学号是最大值,此时插在链表的最后面
q->next=node;
}
return true;
}
bool deleteNode(Link head,char no[])
{
//按照给定的学号删除学生记录,如果删除成功返回true,如果没找到学号返回false
//输入要处理的学号
inputStudentNo("删除",no);
return false;
}
bool queryNode(Link head,char no[])
{
//按照给定的学号查询学生记录,如果查询成功返回true,如果没找到学号返回false
//输入要处理的学号
inputStudentNo("查询",no);
return false;
}
bool modifyNode(Link head,char no[])
{
//按照给定的学号找到学生记录节点,如果修改成功返回true,如果没找到学号返回false
//输入要处理的学号
inputStudentNo("修改",no);
return false;
}
int countNode(Link head)
{
//统计学生的人数,扫描链表统计节点个数,返回节点数
//Link p;
//int count = 0;
//p = head->next;
return false;
}
void clearLink(Link head)
{
//Link q,p;
//遍历链表,用free语句删除链表中用malloc建立起的所有节点
}
int main()
{
int select;
int count;
Link head; //定义链表
char no[20];
//建立head头结点,在这个程序中head指向头结点,头结点data部分没有内容,其后续节点才有真正的
head = (Link)malloc(sizeof(Node));
head->next = NULL;
while(1)//死循环
{
myMenu();
printf("\n请输入你的选择(1-7):"); //显示提示信息
scanf("%d",&select);
switch(select)
{
case 1:
//增加学生记录
if(addNode(head))
printf("成功插入一个学生记录。\n\n");
break;
case 2:
//删除学生记录
if(deleteNode(head,no))
printf("成功删除一个学生记录。\n\n");
else
printf("没有找到要删除的学生节点。 \n\n");
case 3:
//查询学生记录
if(queryNode(head,no))
printf("成功找到学生记录。\n\n");
else
printf("没有找到学生记录。\n\n");
break;
case 4:
//修改学生记录
if(modifyNode(head,no))
printf("成功修改一个学生记录。\n\n");
else
printf("没有找到要修改的学生节点。\n\n");
break;
case 5:
//统计学生人数
count = countNode(head);
printf("学生人数为:%d\n\n",count);
break;
case 6:
//显示学生记录
displayNode(head);
break;
case 7:
//退出前清除链表中的所有节点
clearLink(head);
return 0;
default:
printf("输入不正确,应该输入0~7之间的数。\n\n");
break;
}
}
return 0;
}