资讯 小学 初中 高中 语言 会计职称 学历提升 法考 计算机考试 医护考试 建工考试 教育百科
栏目分类:
子分类:
返回
空麓网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
空麓网 > 计算机考试 > 软件开发 > 后端开发 > C/C++/C#

【用双向带头循环链表实现通讯录】

C/C++/C# 更新时间: 发布时间: 计算机考试归档 最新发布

【用双向带头循环链表实现通讯录】

小伙伴们好啊!
之前写过一个通讯录管理系统,今天,我用另一种方法----双向带头循环链表来实现这个项目,顺便也来复习一下其中的知识点。
希望对大家的课程设计作业有帮助!

项目要求:

用C语言编写一个通讯录程序,要求能够实现对联系人信息的增删查改、排序、清空,将联系人信息保存到文件中,下一次使用时可以读取文件中信息

直接上代码

contact.h

#define _CRT_SECURE_NO_WARNINGS
#pragma once

#define NAME_MAX 20//姓名数组最大空间
#define SEX_MAX 4//性别数组最大空间
#define TEL_MAX 14//电话
#define ADDR_MAX 30//地址

#include 
#include 
#include 
#include 

typedef struct Contact//创建联系人结构体
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	char tele[TEL_MAX];
	char addr[ADDR_MAX];
	int age;
}Contact;

typedef struct Peoinfo//创建结点结构体
{
	Contact contact;
	struct Peoinfo* prev;//存储下一个结点地址
	struct Peoinfo* next;//存储上一个结点地址
}Peoinfo;

//对结点初始化
Peoinfo* Init();
//创建新结点
Peoinfo* Create();
//对链表进行尾插
void PushBack(Peoinfo* phead);
//退出程序
void Exit(Peoinfo* phead);
//增添联系人
void Add(Peoinfo* phead);
//删除联系人
void Dele(Peoinfo* phead);
//查找联系人
void Search(Peoinfo* phead);
//修改联系人信息
void Multify(Peoinfo* phead);
//显示所有联系人信息
void Show(Peoinfo* phead);
//对联系人按名字首字母排序
void Sort(Peoinfo* phead);
//清空通讯录
void Empty(Peoinfo* phead);
//保存通讯录内容到文件中
void Save(Peoinfo* phead);
//加载文件中信息到通讯录
void Load(Peoinfo* phead);
//辅助菜单
void Menu2();

contact.c

#include "contact.h"

Peoinfo* Init()
{
	//创建头结点,该结点不存储有效数据,用做哨兵位
	Peoinfo* phead = (Peoinfo*)malloc(sizeof(Peoinfo));
	//判断空间是否开辟成功
	if (phead == NULL)
	{
		printf("malloc failed!n");
		return;
	}
	else
	{
		//将头结点的next和prev指向自己,保证双向循环结构
		phead->next = phead;
		phead->prev = phead;
		return phead;
	}
}

Peoinfo* Create()
{
	//为新节点开辟空间
	Peoinfo* peo = (Peoinfo*)malloc(sizeof(Peoinfo));
	if (peo == NULL)
	{
		printf("malloc failed!n");
		return;
	}
	else
	{
		//输入新建联系人的信息
		printf("请输入联系人姓名:>");
		scanf("%s", peo->contact.name);
		printf("请输入联系人性别:>");
		scanf("%s", peo->contact.sex);
		printf("请输入联系人电话:>");
		scanf("%s", peo->contact.tele);
		printf("请输入联系人地址:>");
		scanf("%s", peo->contact.addr);
		printf("请输入联系人年龄:>");
		scanf("%d", &peo->contact.age);
	}
	//先将新结点的next和prev指向自己,避免出现野指针
	peo->next = peo;
	peo->prev = peo;
	return peo;
}

void PushBack(Peoinfo* phead)
{
	assert(phead);
	//先创建结点并输入信息,得到新节点的地址
	Peoinfo* peo = Create();
	//将新节点尾插到最后,并调整新节点和头结点的next和prev,保证双向循环结构
	peo->next = phead;
	peo->prev = phead->prev;
	phead->prev->next = peo;
	phead->prev = peo;
}

void Exit(Peoinfo* phead)
{
	assert(phead);
	//释放头结点,并将其置空
	free(phead);
	phead = NULL;
	printf("退出程序!n");
	//退出程序
	exit(0);
}

void Add(Peoinfo* phead)
{
	int n = 0;
	printf("请输入要增加的联系人的数量:>");
	scanf("%d", &n);
	int i = 0;
	//创建n个新结点并输入信息
	for (i = 0; i < n; i++)
	{
		printf("请输入第%d位联系人信息:>n", i + 1);
		//每创建一个新结点,就将其尾插到链表最后
		PushBack(phead);
	}
	printf("添加成功!n");
}

void Dele(Peoinfo* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("当前联系人信息为空!n");
		return;
	}
	else
	{
		char name[NAME_MAX];
		printf("请输入要删除的联系人姓名:>");
		scanf("%s", name);
		//定义指针,用来记录遍历的结点的位置
		Peoinfo* cur = phead->next;
		while (cur != phead)
		{
			if (strcmp(name, cur->contact.name) == 0)
			{
				//调整该结点前后两个结点的next和prev,使前后两个结点相连接
				cur->next->prev = cur->prev;
				cur->prev->next = cur->next;
				//释放该结点
				free(cur);
				cur = NULL;
				printf("删除成功!n");
				return;
			}
			cur = cur->next;
		}
		printf("删除成功!n");
	}
}

void Search(Peoinfo* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("当前通讯录为空,请先存储信息!n");
		return;
	}
	else
	{
		char name[NAME_MAX];
		printf("请输入要查找的联系人的姓名:>");
		scanf("%s", &name);
		printf("%10s %10s %10s %20s %20sn", "姓名", "性别", "年龄", "电话", "地址");
		Peoinfo* cur = phead->next;
		while (cur != phead)
		{
			if (strcmp(name, cur->contact.name) == 0)
			{
				printf("%10s %10s %10d %20s %20sn", cur->contact.name, cur->contact.sex, cur->contact.age, 
													 cur->contact.tele, cur->contact.addr);
				return;
			}
			cur = cur->next;
		}
		printf("该联系人不存在!n");
		return;
	}
}

void Multify(Peoinfo* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("当前通讯录为空,请先添加联系人!n");
		return;
	}
	else
	{
		Peoinfo* cur = phead->next;
		char name[NAME_MAX];
		printf("请输入您要修改的联系人的名字:>");
		scanf("%s", &name);
		while (cur != phead)
		{
			if (strcmp(name, cur->contact.name) == 0)
			{
				int input = 0;
				printf("该联系人当前信息如下:n");
				printf("%10s %10s %10s %20s %20sn", "姓名", "性别", "年龄", "电话", "地址");
				printf("%10s %10s %10d %20s %20sn", cur->contact.name, cur->contact.sex, cur->contact.age, 
													 cur->contact.tele, cur->contact.addr);
				do
				{
					//利用辅助菜单,改变用户想要改变的信息
					Menu2();
					printf("请选择要修改的信息:>");
					scanf("%d", &input);
					//利用do while循环和switch语句可完成多次修改
					switch (input)
					{
					case 1:
						printf("请输入修改后的姓名:>");
						scanf("%s", &cur->contact.name);
						printf("修改成功!n");
						break;
					case 2:
						printf("请输入修改后的性别:>");
						scanf("%s", &cur->contact.sex);
						printf("修改成功!n");
						break;
					case 3:
						printf("请输入修改后的年龄:>");
						scanf("%d", &cur->contact.age);
						printf("修改成功!n");
						break;
					case 4:
						printf("请输入修改后的电话:>");
						scanf("%s", &cur->contact.tele);
						printf("修改成功!n");
						break;
					case 5:
						printf("请输入修改后的地址:>");
						scanf("%s", &cur->contact.addr);
						printf("修改成功!n");
						break;
					case 0:
						printf("退出修改!n");
						return;
					default:
						printf("选择错误,请重新选择!n");
						//若输入错误,自动重新执行该函数
						Multify(phead);
					}
				} while (input);
				return;
			}
			cur = cur->next;
		}
		printf("该联系人不存在!n");
		return;
	}
}

void Show(Peoinfo* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("当前联系人数为0,请先存储联系人信息!n");
		return;
	}
	else
	{
		Peoinfo* cur = phead->next;
		printf("%10s %10s %10s %20s %20sn", "姓名", "性别", "年龄", "电话", "地址");
		//遍历链表并打印
		while (cur != phead)
		{
			printf("%10s %10s %10d %20s %20sn", cur->contact.name, cur->contact.sex, cur->contact.age, 
												 cur->contact.tele, cur->contact.addr);
			cur = cur->next;
		}
	}
}

void Sort(Peoinfo* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("当前通讯录为空,请先存储联系人信息!n");
		return;
	}
	else
	{
		//从第二个有效结点开始和第一个有效结点比较
		Peoinfo* first = phead->next;
		Peoinfo* cur = phead->next->next;
		while (cur != phead)
		{
			if (strcmp(cur->contact.name, first->contact.name) < 0)
			{
				//若后面的结点名字首字母小于第一个有效结点
				//则将该结点变为第一个有效结点
				//即调整头结点和第一个有效结点,该结点和该结点前后两个结点的next和prev,保证双向循环结构
				cur->prev->next = cur->next;
				cur->next->prev = cur->prev;
				phead->prev = cur->next;
				cur->next = first;
				cur->prev = phead;
				phead->next = cur;
				first->prev = cur;
			}
			cur = cur->next;
		}
		printf("排序成功!n");
	}
}

void Empty(Peoinfo* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("当前通讯录为空!n");
		return;
	}
	else
	{
		//释放头结点就相当于释放所有结点
		free(phead->next);
		phead->next = phead;
		phead->prev = phead;
		printf("清空成功!n");
	}
}

void Save(Peoinfo* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("当前通讯录为空,请先添加联系人信息!n");
		return;
	}
	else
	{
		//判断文件打开是否成功
		FILE* fp = fopen("contact.dat", "w");
		if (fp == NULL)
		{
			printf("保存失败!n");
			return;
		}
		else
		{
			Peoinfo* cur = phead->next;

			while (cur != phead)
			{
				//从第一个有效结点开始,依次将有效数据存入文件中
				fwrite(&cur->contact, sizeof(Peoinfo), 1, fp);
				cur = cur->next;
			}
			printf("保存成功!n");
			fclose(fp);
			fp = NULL;
		}
	}
}

void Load(Peoinfo* phead)
{
	//分别为需要加载的数据创建空间和结点
	Peoinfo* head = (Peoinfo*)malloc(sizeof(Peoinfo));
	Contact* con = (Contact*)malloc(sizeof(Contact));
	Peoinfo* p;
	Peoinfo* q;
	p = q = head;
	FILE* fp = fopen("contact.dat", "r");
	if (fp == NULL)
	{
		printf("加载失败!n");
		return;
	}

	while (fscanf(fp, "%s%s%s%s%d", con->name, con->sex, con->tele, con->addr, &con->age) != EOF)
	{
		//读取有效信息
		q = (Peoinfo*)malloc(sizeof(Peoinfo));
		q->contact = *con;
		p->next = q;
		p = q;
	}
	//将读取到的信息存入结点并进行尾插
	p->next = phead;
	p->prev = phead->prev;
	phead->prev->next = p;
	phead->prev = p;
	printf("加载成功!n");
}

void Menu2()
{
	//辅助菜单,在修改函数中被调用
	printf("***********************n");
	printf("**** 1.姓名 2.性别 ****n");
	printf("**** 3.年龄 4.电话 ****n");
	printf("**** 5.地址 0.退出 ****n");
	printf("***********************n");
}

work.c

#include "contact.h"

void Menu()
{
	printf("***********************************n");
	printf("******   0.退出     1.添加   ******n");
	printf("******   2.删除     3.查找   ******n");
	printf("******   4.修改     5.显示   ******n");
	printf("******   6.排序     7.清空   ******n");
	printf("******   8.加载     9.保存   ******n");
	printf("***********************************n");
}

void Work()
{
	//先进行初始化,创建头结点
	Peoinfo* phead = Init();
	int choice = 0;
	//用函数指针数组简化代码,减少代码冗余
	void (*p[10])(Peoinfo* phead) =
	{
		Exit,
		Add,
		Dele,
		Search,
		Multify,
		Show,
		Sort,
		Empty,
		Load,
		Save
	};
	do
	{
		Menu();
		printf("请选择:>");
		scanf("%d", &choice);
		if (choice >= 0 && choice <= 9)
		{
			//对函数指针进行解引用,调用其指向的函数并传参
			(*p[choice])(phead);
		}
		else
		{
			printf("选择错误,请重新选择!n");
			return;
		}
	} while (choice);
}

int main()
{
	Work();
	return 0;
}

运行结果截图:



由于之前已经解释过项目整体了,这里就不再赘述了,希望对大家有帮助!!!

转载请注明:文章转载自 http://www.konglu.com/
本文地址:http://www.konglu.com/it/938721.html
免责声明:

我们致力于保护作者版权,注重分享,被刊用文章【【用双向带头循环链表实现通讯录】】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理,本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2023 成都空麓科技有限公司

ICP备案号:蜀ICP备2023000828号-2