【数据结构|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;
}

后言

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

  • 63
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值