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

news2024/9/27 15:25:29

🍔链表基础知识

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);
}

🌈测试结果

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/832537.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信号源功率输出是什么意思(功率信号源)

信号源功率输出是指信号源能够输出的最大功率。在无线电通信和电子工程领域中&#xff0c;信号源功率输出是一项非常重要的参数&#xff0c;它对信号传输距离、接收灵敏度、噪声抑制等方面都有着重要的影响。 信号源功率输出与信号传输距离密切相关。在无线电通信中&#xff0c…

二叉搜索树与双向链表(牛客网 和 剑指 Offer同类型题)

文章目录 JZ36 二叉搜索树与双向链表&#xff08;牛客网&#xff09;剑指 Offer 36. 二叉搜索树与双向链表 JZ36 二叉搜索树与双向链表&#xff08;牛客网&#xff09; 题目链接 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。如下图所示 注意: 1…

亚马逊品牌推荐金计划:通过亚马逊外营销活动赚取奖金!

亚马逊美国站发布公告称新推出的品牌推荐金计划可以让卖家在通过亚马逊外营销活动的销售中获得奖金&#xff0c;当卖家将非亚马逊营销流量引导至亚马逊时&#xff0c;您将获得促销产品以及客户在接下来的两周内购买的任何品牌产品平均销售额的10%的奖金&#xff0c;以下是公告内…

【C语言初阶】使用指针求字符串长度(五个版本盘点总结)

在代码的题目中&#xff0c;我们经常会遇到需要自己手写函数&#xff0c;求字符串长度的情况&#xff0c;那么今天博主就带大家一起盘点五种求字符串长度的写法 版本一&#xff1a;判断累加法 逻辑&#xff1a;由于字符串的末位是\0&#xff0c;且\0不计入字符串长度&#xf…

docker logs 使用说明

docker logs 可以查看某个容器内的日志情况。 前置参数说明 c_name容器名称 / 容器ID logs 获取容器的日志 , 命令如下&#xff1a; docker logs [options] c_name option参数&#xff1a; -n 查看最近多少条记录&#xff1a;docker logs -n 5 c_name--tail与-n 一样 &#…

C高级 作业 day2 8/3

1.脑图 2.递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位 #include <myhead.h>void solute(int a) {if(a<10&&a>0) //如果是一位数{printf("%2d\t",a);return;}else //两位及以上{ solute(a/10);printf("%d\t",a%10…

1345:香甜的黄油(Dijkstra)---信息学奥赛一本通

【题目描述】 农夫John发现做出全威斯康辛州最甜的黄油的方法&#xff1a;糖。把糖放在一片牧场上&#xff0c;他知道N&#xff08;1≤N≤500&#xff09;只奶牛会过来舔它&#xff0c;这样就能做出能卖好价钱的超甜黄油。当然&#xff0c;他将付出额外的费用在奶牛上。 农夫Jo…

Electron 开发,报handshake failed; returned -1, SSL error code 1,错误

代码说明 在preload.js代码中&#xff0c;暴露参数给渲染线程renderer.js访问&#xff0c; renderer.js 报&#xff1a;ERROR:ssl_client_socket_impl.cc(978)] failed; returned -1, SSL error code 1,错误 问题原因 如题所说&#xff0c;跨进程传递消息&#xff0c;这意味…

Matlab对TMS320F28335编程--SVPWM配置互补PWM输出

前言 F28335中断 目的&#xff1a;FOC的核心算法及SVPWM输出&#xff0c;SVPWM的载波频率10kHz&#xff0c;SVPWM的每个周期都会触发ADC中断采集相电流&#xff0c;SVPWM为芯片ePWM4、5、6通道&#xff0c;配置死区 1、配置中断SVPWM进ADC中断&#xff0c;查上表知CPU1,PIE1 …

一个计算机专业的学生数据结构这门课学到什么程度才能算学的还不错?

数据结构之所以重要是因为它处于算法中的基础地位&#xff0c;与解决实际问题关系密切&#xff1b;而之所以不重要是因为课本上能学到的所有实现都已经有人造过轮子了&#xff0c;甚至已经作为很多语言的标准API存在了。 换句话来说&#xff0c;在以后的编码生涯中&#xff0c…

Qt tabwidget中插入widget

一、简单介绍 QT->tabWidget&#xff1a;标签页面。 在ui中通过工具栏自定义拉取控件&#xff0c;其中tabwidget可以可以创建多个标签页面&#xff0c;默认生成两个tab_widget(tab_1/tab_2)。并且可以在ui中右键自由添加控制删除等标签页&#xff0c;切换标签页就是切换widg…

TensorRT学习笔记--基于TensorRT部署YoloV3, YoloV5和YoloV8

目录 1--完整项目 2--模型转换 3--编译项目 4--序列化模型 5--推理测试 1--完整项目 以下以 YoloV8 为例进行图片和视频的推理&#xff0c;完整项目地址如下&#xff1a;https://github.com/liujf69/TensorRT-Demo git clone https://github.com/liujf69/TensorRT-Demo.…

Kubernetes高可用集群二进制部署(六)Kubernetes集群节点添加

Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署&#xff08;一&#xff09;主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署&#xff08;二&#xff09;ETCD集群部署 Kubernetes高可用集群二进制部署&#xff08;三&#xff09;部署…

8.3 C高级 Shell脚本

写一个脚本&#xff0c;包含以下内容&#xff1a; 显示/etc/group文件中第五行的内容创建目录/home/ubuntu/copy切换工作路径到此目录赋值/etc/shadow到此目录&#xff0c;并重命名为test将当前目录中test的所属用户改为root将test中其他用户的权限改为没有任何权限 #!/bin/b…

1016. 子串能表示从 1 到 N 数字的二进制串;2601. 质数减法运算;1824. 最少侧跳次数

1016. 子串能表示从 1 到 N 数字的二进制串 核心思想&#xff1a;第一种方法&#xff0c;直接将1-n转化为二进制数看它是否是在s中&#xff0c;第二种方法&#xff0c;将s的二进制字串转换为数字存在哈希表中&#xff0c;看它是否满足长度为n,然后具体的方法就是枚举字符串的左…

uC-OS2 V2.93 STM32L476 移植:串口打印篇

前言 前几篇已经 通过 STM32CubeMX 搭建了 NUCLEO-L476RG 的 STM32L476RG 的 裸机工程&#xff0c;下载了 uC-OS2 V2.93 的源码&#xff0c;并把 uC-OS2 的源文件加入 Keil MDK5 工程&#xff0c;通过适配 Systick 系统定时器与 PendSV 实现任务调度&#xff0c;初步让 uC-OS2 …

以技术驱动反欺诈,Riskified 为企业出海保驾护航

如今&#xff0c;全球对于线上消费的需求日益增长&#xff0c;各类新型支付方式也层出不穷。在国内&#xff0c;线上支付有着较为完善的法律及监管条例&#xff0c;格局基本已定型。但对于出海商家而言&#xff0c;由于不同国家和地区的支付规则和监管机制不同&#xff0c;跨境…

老师辅助神器:人工智能知道学生是否在集中注意力

面部识别技术于20世纪60年代引入&#xff0c;在人工智能寒冬中萎靡不振&#xff0c;近年来在日益强大的深度神经网络的推动下已经起飞。面部识别已应用于Face ID设备解锁功能&#xff0c;公共安全服务&#xff0c;智能支付系统等。在泰勒斯威夫特&#xff08;Taylor Swift&…

C高级第二讲

1、思维导图 2、递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位 /* ---------------------------------author&#xff1a;YoungZorncreated on 2023/8/3 18:40.--------------------------------- */ #include<iostream>using namespace std;//递归实现…

s7200Smart

一、介绍 二、通讯 rs485 modbus通讯口