带头单链表,附带完整测试程序

🍔链表基础知识

1.概念:链表是由多个节点链接构成的,节点包含数据域和指针域,指针域上存放的指针指向下一个节点
2.链表的种类:按单向或双向、带头或不带头、循环或不循环分为多个种类
3.特点:无法直接找到结点,但可以快速插入、删除节点,尤其适用于头插,效率块于其它方式
4.应用:p2p网络,文件系统,作为其他数据结构的基础数据结构

👻本篇文章是关于带头单向链表的

🍟带头单链表

👻头可以理解为哨兵位。哨兵不用干其他活,放好哨就行,而哨兵位不存储有效数据,只提供指向下一个节点(即存放有效数据的第一个节点)的指针。

👻以一个学生信息表为例,用单链表存储学生信息

🍟定义部分

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct {
	int age;
	int height;
	double weight;
}Student;
typedef struct node {
	Student stu;
	struct node* next;
}Node;

🍟函数部分

🥤一、辅助操作函数

🍕1.结点的拷贝函数
//节点的拷贝函数
void CopyValue(Student* s1, Student* s2){
	s1->age = s2->age;
	s1->height = s2->height;
	s1->weight = s2->weight;
}


🍕2.输入学生信息函数
//输入学生信息
void InputValue(Student* stu) {
	if (!stu)return;
	printf("学生的年龄,身高,体重\n");
	scanf("%d%d%lf", &stu->age, &stu->height, &stu->weight);
}
🍕3.打印信息函数
//输出每个节点的信息
void DisplayNode(Student* stu) {
	printf("年龄:%-5d 身高:%-5d 体重:%-5lf\n", stu->age, stu->height, stu->weight);
}
🍕4.打印单链表
//打印单链表
void DisplayLink(Node* head) {
	//1.判断链表是否为空,为空直接返回
	if (!head)return;
	//2.打印每个节点信息
	Node* p = head->next;
	while (p != NULL) {
		DisplayNode(&p->stu);
		p = p->next;
	}
}

🥤二、创建节点,创建、销毁链表

🍕1.创建节点
//创建节点
Node* CreateNode(Student* stu){
	//1.申请一份空间
	Node* node = (Node*)malloc(sizeof(Node));
	//2.判断空间是否申请成功
	if (!node) {
		printf("内存不足\n");
		return NULL;
	}
	//3.初始化结节中的数据域,即学生信息
	CopyValue(&node->stu, stu);
	node->next = NULL;
	//4.返回创建的结点
	return node;
}
🍕2.创建链表
//创建链表
Node* CreateLink() {
	Student stu = { 0,0,0 };//该组数据不是有效数据
	//1.创建头结点
	Node* head = CreateNode(&stu);
	if (!head) exit(-1);
	//2.创建其他结点,连接到头结点构成链表
	int tag = 0;
	Node* pnew, * tail = head;
	printf("是否创建新结点,继续添加输入1,否则输入0\n");
	scanf("%d", &tag);
	while (tag != 0) {
		InputValue(&stu);
		pnew=CreateNode(&stu);
		tail->next = pnew;
		tail = pnew;
		printf("是否创建新结点,继续添加输入1,否则输入0\n");
		scanf("%d", &tag);
	}
	return head;
}
🍕3.销毁链表
//销毁链表
void FreeLink(Node* head) {
	//1.判断链表是否为空,为空则返回
	if (head == NULL)
		return;
	//如果非空,则逐个结点释放
	Node* p, * q;
	p = head;
	while (p->next != NULL) {
		q = p->next;
		p->next = q->next;
		free(q);
	}
	free(head);
}

🥤三、增删查改——增

🍕在指定节点后面插入节点
//在指定位置插入节点
void InsertNode(Node* head, Node* pnew,int i) {
	//1.判断链表是否为空,为空拒绝插入
	if (!head)return;
	//2.判断结点是否为空,为空拒绝插入
	if (!pnew)return;
	//3.遍历链表,插入指定位置
	Node* p = head;
	for (int n = 0;n < i;n++) {
		p = p->next;
	}
	pnew->next = p->next;
	p->next = pnew;
}

只能对当前指针所在节点处的后面的结点进行操作!!!
如果想在指定节点前面插入节点,只需要改变for循环中的初始值,让指针停留在指定节点的前置节点处,就可以插入到指定节点的前面了

🥤四、增删查改——删

🍕删除指定位置节点
//删除指定位置节点
void DeleteNode(Node* head, int i) {
	//1.判断链表是否为空,为空直接返回
	if (!head)return;
	//2.遍历链表找到要删除的位置
	Node *p=head,*q;
	for (int n = 1;n < i&&p->next!=NULL;n++) {
		p = p->next;//当p等于NULL了,说明把整条链表都遍历完了还没有找到
	}
	//3.如果找到了该位置,删除,找不到就返回
	if (!p)return;
	q = p->next;
	p->next = q->next;
	free(q);
}

只能对当前指针所在节点处的后面的结点进行操作!!!
n从1开始,指针停留位置就是被删除节点的前置节点,从而可以删除掉指定节点;如果n从0开始,指针停留位置是指定节点,此时就无法删除该位置了,此时的指针只能掌控住指向节点之后的节点。

🥤五、增删查改——查与改

🍕比较两节点内容
//比较两节点内容
int Compare(Student* s1, Student* s2) {
	if (s1 == NULL || !s2)return -1;
	if (s1->age == s2->age && s1->height == s2->height && s1->weight == s2->weight)
		return 1;
	return 0;
}
🍕根据指定信息查找结点
//根据指定信息查找节点
int CheckNode(Node* head, Student* stu) {
	//1.链表与查找的节点不为空
	if (!head || !stu)return 0;
	//2.遍历单链表,找到是否有相同的节点
	Node* p = head->next;
	int ret = 1;
	int m = 0;
	while (p!=NULL) {
		m = Compare(&p->stu, stu);
		if (m == 1)return ret;
		p = p->next;
		ret++;
	}
	return 0;
}
🍕改变指定位置的节点信息
//改变指定位置的节点信息
void ModifyValue(Node* head,Student*stu, int input) {
	//1.判断第一个节点是否存在
	if (!head || head->next == NULL)return;
	//2.遍历链表找到节点位置
	Node* p = head->next;
	for (int i = 1;i < input;i++) {
		p = p->next;
	}
	//3.改变信息
	p->stu.age = stu->age;
	p->stu.height = stu->height;
	p->stu.weight = stu->weight;
}

🍟测试带头单链表完整程序

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct {
	int age;
	int height;
	double weight;
}Student;
typedef struct node {
	Student stu;
	struct node* next;
}Node;
//节点的拷贝函数
void CopyValue(Student* s1, Student* s2){
	s1->age = s2->age;
	s1->height = s2->height;
	s1->weight = s2->weight;
}
//输入学生信息
void InputValue(Student* stu) {
	if (!stu)return;
	printf("学生的年龄,身高,体重\n");
	scanf("%d%d%lf", &stu->age, &stu->height, &stu->weight);
}
//输出每个节点的信息
void DisplayNode(Student* stu) {
	printf("年龄:%-5d 身高:%-5d 体重:%-5lf\n", stu->age, stu->height, stu->weight);
}
//创建节点
Node* CreateNode(Student* stu){
	//1.申请一份空间
	Node* node = (Node*)malloc(sizeof(Node));
	//2.判断空间是否申请成功
	if (!node) {
		printf("内存不足\n");
		return NULL;
	}
	//3.初始化节点中的数据域,即学生信息
	CopyValue(&node->stu, stu);
	node->next = NULL;
	//4.返回创建的节点
	return node;
}

//创建链表
Node* CreateLink() {
	Student stu = { 0,0,0 };
	//1.创建头节点
	Node* head = CreateNode(&stu);
	if (!head) exit(-1);
	//2.创建其他节点,连接到头节点构成链表
	int tag = 0;
	Node* pnew, * tail = head;
	printf("是否创建新结点,继续添加输入1,否则输入0\n");
	scanf("%d", &tag);
	while (tag != 0) {
		InputValue(&stu);
		pnew=CreateNode(&stu);
		tail->next = pnew;
		tail = pnew;
		printf("是否创建新结点,继续添加输入1,否则输入0\n");
		scanf("%d", &tag);
	}
	return head;
}
//销毁链表
void FreeLink(Node* head) {
	//1.判断链表是否为空,为空则返回
	if (head == NULL)
		return;
	//如果非空,则逐个节点释放
	Node* p, * q;
	p = head;
	while (p->next != NULL) {
		q = p->next;
		p->next = q->next;
		free(q);
	}
	free(head);
}

//打印单链表
void DisplayLink(Node* head) {
	//1.判断链表是否为空,为空直接返回
	if (!head)return;
	//2.打印每个节点信息
	Node* p = head->next;
	while (p != NULL) {
		DisplayNode(&p->stu);
		p = p->next;
	}
}
//在指定位置插入节点
void InsertNode(Node* head, Node* pnew,int i) {
	//1.判断链表是否为空,为空拒绝插入
	if (!head)return;
	//2.判断结点是否为空,为空拒绝插入
	if (!pnew)return;
	//3.遍历链表,插入指定位置
	Node* p = head;
	for (int n = 0;n < i;n++) {
		p = p->next;
	}
	pnew->next = p->next;
	p->next = pnew;
}
//在指定位置删除结点
void DeleteNode(Node* head, int i) {
	//1.判断链表是否为空,为空直接返回
	if (!head)return;
	//2.遍历链表找到要删除的位置
	Node *p=head,*q;
	for (int n = 1;n < i&&p->next!=NULL;n++) {
		p = p->next;//当p等于NULL了,说明把整条链表都遍历完了还没有找到
	}
	//3.如果找到了该位置,删除,找不到就返回
	if (!p)return;
	q = p->next;
	p->next = q->next;
	free(q);
}
//比较两节点内容
int Compare(Student* s1, Student* s2) {
	if (s1 == NULL || !s2)return -1;
	if (s1->age == s2->age && s1->height == s2->height && s1->weight == s2->weight)
		return 1;
	return 0;
}
//根据指定信息查找节点
int CheckNode(Node* head, Student* stu) {
	//1.链表与查找的节点不为空
	if (!head || !stu)return 0;
	//2.遍历单链表,找到是否有相同的节点
	Node* p = head->next;
	int ret = 1;
	int m = 0;
	while (p!=NULL) {
		m = Compare(&p->stu, stu);
		if (m == 1)return ret;
		p = p->next;
		ret++;
	}
	return 0;
}

//改变指定位置的节点信息
void ModifyValue(Node* head,Student*stu, int input) {
	//1.判断第一个节点是否存在
	if (!head || head->next == NULL)return;
	//2.遍历链表找到节点位置
	Node* p = head->next;
	for (int i = 1;i < input;i++) {
		p = p->next;
	}
	//3.改变信息
	p->stu.age = stu->age;
	p->stu.height = stu->height;
	p->stu.weight = stu->weight;
}

int main() {
	//1.初步创建链表并插入初始信息,最终打印
	Node* head = CreateLink();
	DisplayLink(head);
	
	//2.插入新节点
	printf("请输入要插入的位置:\n");
	int input = 0;
	scanf("%d", &input);//得到位置
	Student stu;
	printf("请输入学生信息:\n");
	InputValue(&stu);//得到信息
	Node* pnew = CreateNode(&stu);//得到新的结点
	InsertNode(head, pnew, input);
	printf("以下是插入信息后的信息表\n");
	DisplayLink(head);

	//3.在指定位置删除结点
	printf("请输入要删除的位置\n");
	int input2 = 0;
	scanf("%d", &input2);
	DeleteNode(head, input2);
	printf("以下是删除信息后的信息表\n");
	DisplayLink(head);

	//4.根据指定信息查找结点
	int input3;
	do{
		if (head->next == NULL) {
			printf("表中已无信息\n");
			break;
		}
		printf("请输入要查找的学生信息\n");
		Student stu2;
		InputValue(&stu2);
		int ret = CheckNode(head, &stu2);
		if (ret != 0)
			printf("找到了,该学生编号为:%d\n", ret);
		else
			printf("无该学生信息\n");

		printf("是否继续查找,是输1,否输0:");
		input3 = 0;
		scanf("%d", &input3);
	} while (input3 == 1);

	//5.改变指定位置的节点信息
	printf("请输入要改变的结点位置:");
	int input4 = 0;
	scanf("%d", &input4);
	printf("请输入改变后的信息\n");
	Student stu3;
	InputValue(&stu3);
	ModifyValue(head,&stu3,input4);
	printf("以下是改变信息后的信息表\n");
	DisplayLink(head);

	//销毁链表
	FreeLink(head);
}

🌈测试结果

在这里插入图片描述
在这里插入图片描述

如有错误请指正哦,感谢❤️

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值