数据结构案例教程————链表(注释解析版)

//头文件引用
#include<iostream>
#include<stdlib.h>
#include<string.h>

//定义学生类结构体
struct Student {
	int no;    //学号(整型)
	char name[10];   // 名字(字符串)
	char sex;      //性别(字符)
	int age;      // 年龄(整型)
	char major[12];   // 专业(字符串)
};



typedef struct Student ElemType;//这段代码定义了两个结构体:ElemType 和 Node
typedef struct Node {
	ElemType data;
	struct Node* next;  /*ElemType 结构体表示一个学生,包含学生的各种信息,如姓名、学号、年龄等。
                          Node 结构体表示链表中的一个节点,包含节点的数据 data 和指向下一个节点的指针 next*/
}Node, * LinkList;     //通过 typedef 定义了一个指向 Node 结构体的指针类型 LinkList,表示一个链表



void copy(struct Student& s1, struct Student s2) {  //定义函数copy,其中包含两个结构体类型的变量参数
	s1.no = s2.no;      //赋值
	strcpy(s1.name, s2.name);  //将s2的name字符串成员变量复制到s1中
	s1.sex = s2.sex;    //赋值
	s1.age = s2.age;    //赋值
	strcpy(s1.major, s2.major);   //将s2的major字符串成员变量复制到s1中
}   //该函数的作用是将s2的每个成员变量的值复制到s1对应的成员变量中


//这段代码是实现一个带头结点的单链表的创建过程
void CreatList_L(LinkList& L, ElemType A[], int n) { //L:指向头结点的指针、A[]:输入数组、n:输入数组中元素个数
	LinkList s;
	L = new Node;   //创建头结点 L
	L->next = NULL;   //将其 next 指针指向 NULL。
	for (int i = n - 1; i >= 0; i--) {  //从数组 A 中倒序遍历每个元素
		s = new Node;   //创建一个新的节点 s
		copy(s->data, A[i]);    //并将 s 的数据域赋值为 A[i]。
		s->next = L->next;      //将 s 的 next 指针指向当前头结点 L 的下一个节点(即原本在 L 后面的节点)                
		L->next = s;          //并将 L 的 next 指针指向 s,相当于将 s 插入到了链表的头部
	} //循环结束后,链表的头结点 L 就指向了最后一个插入的节点
}


//这段代码是实现了一个遍历带头结点的单链表的操作
void Show_L(LinkList L) {  //L:指向头结点的指针
	LinkList p = L->next;  //获取链表中第一个有效节点 p(即头结点的下一个节点)。
	while (p) {
		cout << p->data.no << " " << p->data.name << " " << p->data.sex << " " << p->data.age << " " << p->data.major << endl;
		p = p->next;//循环遍历链表中的每个有效节点,输出其数据域中的成员变量 no、name、sex、age 和 major 的值,并将 p 指向下一个节点。
	}
	cout << endl;  //输出完最后一个节点后,输出换行符,应该是为了区分不同输出结果
}  //这段代码中输出的数据依赖于链表节点的数据结构


//这段代码是实现了一个带头结点的单链表的长度计算
int ListLength_L(LinkList L) {   //L:指向头结点的指针
	LinkList p = L->next; int k = 0; //获取链表中第一个有效节点 p(即头结点的下一个节点)
	while (p) {
		k++; p = p->next;
	}  //循环遍历链表中的每个有效节点,每访问一个有效节点,k 的值加 1,p 指向下一个节点
	return k;   //返回单链表中元素的个数(不包括头结点),即返回计数器 k 的值,即为单链表的长度。
}  //这段代码中的计算逻辑依赖于链表节点的数据结构和指针域的正确性,如果链表节点定义或指针域有误可能导致计算结果不准确


//这段代码是实现了一个带头结点的单链表的元素定位操作
int LocateElem_L(LinkList L, ElemType e) { //L:指向头结点的指针,e:待查找的目标元素
	LinkList p = L->next;  //获取链表中第一个有效节点 p(即头结点的下一个节点)
	int index = 1;
	while (p && p->data.no != e.no) {//循环遍历链表中的每个有效节点,每访问一个有效节点,判断其数据域中的成员变量 no 是否等于目标元素 e 中的 no
		p = p->next;
		index++;
	}
	if (p)return index;//若找到目标元素,则返回它在链表中第一次出现的位置(从 1 开始计数)
	else return 0;//若未找到目标元素,则返回 0
}


//这段代码是实现了一个带头结点的单链表的元素获取操作
LinkList GetElem_L(LinkList L, ElemType e) { //L:指向头结点的指针,e:待查找的目标元素
	LinkList p = L->next;   //获取链表中第一个有效节点 p(即头结点的下一个节点)。
	while (p && p->data.no != e.no)//循环遍历链表中的每个有效节点,每访问一个有效节点,判断其数据域中的成员变量 no 是否等于目标元素 e 中的 no
		p = p->next;
	return p;//若找到目标元素,则返回指向该元素节点的指针。若未找到目标元素,则返回空指针。
}//这段代码中的元素比较和链表节点的数据结构依赖关系密切


//这段代码是实现了一个带头结点的单链表的元素插入操作
void ListInsert_L(LinkList L, int i, ElemType e) {//L:指向头结点的指针,i:插入位置,从 1 开始计数,e:待插入的元素值
	LinkList p = L, s;
	int j = 0;
	while (p && j < i - 1) {
		p = p->next;
		j++;
	}//获取链表中第一个有效节点 p(即头结点的下一个节点),及插入位置 i 对应的前一个节点 p
	if (!p) {
		cout << "插入位置非法"; exit(1);
	}//判断插入位置是否合法,如果插入位置非法,则输出错误信息,程序结束。
	else {//如果插入位置合法,则为待插入元素值创建新节点 s。
		s = new Node;
		copy(s->data, e);
		s->next = p->next;
		p->next = s;
	}//将节点 s 插入到链表中,即将前一个节点 p 的 next 指针指向 s,将节点 s 的 next 指针指向原来的后继节点
}//这段代码中的元素插入依赖于链表的节点结构及其数据结构,并且要求链表节点的数据结构中含有 copy() 函数用于将元素值复制到链表节点数据域中。


//这段代码是实现了一个带头结点的单链表的元素删除操作
void ListDelete_L(LinkList L, int i) {//L:指向头结点的指针,i:待删除元素的位置,从 1 开始计数
	LinkList p = L, q;
	int j = 0;
	while (p && j < i - 1) {
		p = p->next;
		j++;
	}  //获取链表中第一个有效节点 p(即头结点的下一个节点),及待删除的前一个节点 p
	if (!p || !p->next) {
		cout << "删除位置非法"; exit(1);
	}//判断待删除元素的位置是否合法,如果不合法,则输出错误信息,程序结束
	else {//如果待删除元素的位置合法
		q = p->next;
		p->next = q->next;//获取待删除元素节点 q,将前一个节点 p 的 next 指针指向待删除节点 q 的后继节点
		delete q;   //释放待删除节点 q 的内存空间
	}
}//这段代码中的元素删除依赖于链表的节点结构及其数据结构,并且要求链表节点的空间是通过 new 来申请的


//这段代码是实现了一个带头结点的单链表的销毁操作
voidDestroyList_L(LinkList& L) { //L:指向头结点的指针
	LinkList p = L, q;
	while (p) {// 获取链表中第一个有效节点 p(即头结点的下一个节点),以及其后继节点 q
		q = p;
		p = p->next;
		delete q;   //释放节点 q 的内存空间
	}  //将节点指针 p 指向下一个节点,重复上述步骤直到所有节点空间均被释放
	L = NULL; //将头结点指针 L 指向 NULL,表示链表已经被销毁
}  //该代码中要求链表节点的空间是通过 new 来申请的,并且使用了引用传参来修改头指针 L 的值



//它通过创建一个带头结点的单链表存储学生信息,并展示出这些学生的信息
void Create(LinkList& L) {  //L:指向头结点的指针
	int n;   //从用户输入中获取学生的数量 n
	struct Student st[100];//定义了一个 Student 类型的数组 st,用于存储每位学生的信息。
	cout << "请输入学生人数:";
	cin >> n;
	cout << "请输入每位学生的信息:";
	for (int i = 0; i < n; i++)//过循环,依次从用户输入中读取每位学生的信息(包括学号、姓名、性别、年龄和专业),并将其存储至 st 数组中。
		cin >> st[i].no >> st[i].name >> st[i].sex >> st[i].age >> st[i].major;
	CreateList_L(L, st, n);//调用 CreateList_L 函数创建一个带头结点的单链表,并将学生信息存储其中
	cout << "学生信息如下:\n";
	Show_L(L);//输出所有学生的信息,即调用 Show_L 函数展示刚刚创建的单链表中的数据
}


//它实现了对带头结点的单链表中的学生信息进行查询
void Locate(LinkList L) {  //L:指向头结点的指针
	struct Student s;
	cout << "请输入需查询学生的学号:";
	cin >> s.no; //从用户输入中获取需要查询的学生的学号 s.no
	LinkList p = GetElem_L(L, s);//调用 GetElem_L 函数在链表 L 中查找学号为 s.no 的学生节点 p,并将其赋值给指针 p
	if (p == NULL)  //如果 p 为 NULL,则说明没有找到学号为 s.no 的学生
		cout << "没有此学号的学生\n";
	else//如果 p 不为 NULL,则说明找到了该节点。输出该节点中存储的学生信息(包括学号、姓名、性别、年龄和专业)
		cout << p->data.no << " " << p->data.name << " " << p->data.sex << " " << p->data.age << " " << p->data.major << endl;
}//该代码中使用了结构体来存储每个学生的信息,并且每个学生的信息包括学号、姓名、性别、年龄和专业。


//这段代码实现了一个函数Delete,它从链表L中删除一个学生节点
void Delete(LinkList& L) {
	struct Student s;    //定义了一个学生结构体,包含学生的学号属性no
	cout << "请输入需删除学生的学号:";
	cin >> s.no;      //用户输入需要删除的学生的学号
	int index = LocateElem_L(L, s);     //调用LocateElem_L函数查找该学号在链表中的位置
	if (index == 0)
		cout << "没有此学号的学生\n";//如果该学号不存在,则输出"没有此学号的学生"
	else {
		ListDelete_L(L, index);   //否则,调用ListDelete_L函数删除该节点
		Show_L(L); //调用Show_L函数展示删除后的链表
	}
}


// 这段代码实现了一个函数Insert,它向链表L中插入一个新的学生节点
void Insert(LinkList& L) {
	struct Student s;
	cout << "请输入需插入学生的学号:";
	cin >> s.no;
	cout << "请输入需插入学生的姓名:";
	cin >> s.name;
	cout << "请输入需插入学生的性别:";
	cin >> s.sex;
	cout << "请输入需插入学生的年龄:";
	cin >> s.age;
	cout << "请输入需插入学生的专业:";
	cin >> s.major;
	//用户输入需要插入的学生的学号、姓名、性别、年龄和专业等信息,将这些信息存储在一个学生结构体s中
	int len = ListLength_L(L);  //调用ListLength_L函数获取当前链表的长度len
	ListInsert_L(L, len + 1, s);//调用ListInsert_L函数将学生节点s插入到链表的末尾
	cout << "学生信息如下:\n";
	Show_L(L);  //调用Show_L函数展示插入后的链表
} 


//这段代码实现了一个函数Exit,它用于关闭程序并退出
void Exit(LinkList& L) {
	if (L != NULL)   //如果链表L存在(不为NULL)
		DestroyList_L(L);  //调用DestroyList_L函数销毁链表并释放内存
}
//  可以避免内存泄漏和其它潜在的问题


// 主函数部分
int main() {
	LinkList List = NULL;
	int choice = 0;
	do {
		cout << "**************学生信息管理**************\n";
		cout << "****************1----建立****************\n";
		cout << "****************2----查询****************\n";
		cout << "****************3----删除****************\n";
		cout << "****************4----增加****************\n";
		cout << "****************0----推出****************\n";
		cout << "*****************************************\n";
		cout << "请选择(0-4):";
		cin >> choice;
		switch (choice) {
		case 1:
		{
			Create(List);
			break;
		}
		case 2:
		{
			Locate(List);
			break;
		}
		case 3:
		{
			Delete(List);
			break;
		}
		case 4:
		{
			Insert(List);
			break;
		}
		case 0:
		{
			Exit(List);
			break;
		}
		default:
			cout << "输入错误!\n" << endl;
		}
	} while (choice);
	return 0;
}

觉得有帮助的话就给我点个赞吧!这对我很重要

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼大虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值