数据结构——基于单链表创建通讯录
1. 通讯录的功能
1. contach.c
1. 初始化通讯录
2. 添加通讯录数据
3. 删除通讯录数据
4. 展示通讯录数据
5. 查找通讯录数据
6. 修改通讯录数据
7. 销毁通讯录数据
2. SList.c
1. 尾插
2. 删除指定节点
3. 销毁链表
2. 通讯录的定义
1. contach.h
创建一个结构体,存储联系人数据
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
2. SList.h
创建一个结构体包含两个成员,一个成员存储联系人数据,一个成员存储下一个结点的地址(当下一个节点为空时保存的地址为NULL)
typedef PeoInfo SLTDataType;
struct SListNode
{
SLTDataType data;
struct SListNode* next;
};
3. 通讯录功能实现
1. contach.c
查找通讯录中名字相同的联系人
contact* FindName(contact* con, char name[]) {
assert(con);
while (con) {
if (strcmp(con->data.name, name) == 0) {
return con;
}
con = con->next;
}
return NULL;
}
1. 初始化通讯录
//将contact.text文件中的数据导入到通讯录链表中
void LoadContact(contact** con) {
FILE* pf = fopen("contact.txt", "rb");//打开一个存放通讯录的二进制文件,只读
if (pf == NULL) {
perror("文件打开失败");
return;
}
PeoInfo ps;
//循环读取文件数据
while (fread(&ps, sizeof(ps), 1, pf)) {
SLTPushBack(con, ps);
}
printf("历史数据导入通讯录成功\n");
fclose(pf);//关闭文件
}
//初始化通讯录
void InitContact(contact** con) {
LoadContact(con);
printf("通讯录初始化完成\n");
}
2. 添加通讯录数据
void AddContact(contact** con) {
PeoInfo ps;
printf("请输入姓名:");
scanf("%s", ps.name);
printf("请输入性别:");
scanf("%s", ps.sex);
printf("请输入年龄:");
scanf("%d", &ps.age);
printf("请输入电话:");
scanf("%s", ps.tel);
printf("请输入地址:");
scanf("%s", ps.addr);
SLTPushBack(con, ps);//因为SLTPushBack的第一个参数是 contact** con
}
3. 删除通讯录数据
void DelContact(contact** con) {
char name[NAME_MAX];
printf("输入要删除的姓名:");
scanf("%s", name);
contact* pos = FindName(*con, name);
if (pos) {
SLTErase(con, pos);
printf("删除成功\n");
}
else {
printf("该联系人不存在,无法删除\n");
}
}
4. 展示通讯录数据
void ShowContact(contact* con) {
while (con) {
printf("姓名:%s 性别:%s 年龄:%d 电话:%s 地址:%s\n",
con->data.name,
con->data.sex,
con->data.age,
con->data.tel,
con->data.addr);
con = con->next;
}
}
5. 查找通讯录数据
void FindContact(contact* con) {
char name[NAME_MAX];
printf("输入要查找的姓名:");
scanf("%s", name);
contact* pos=FindName(con, name);
if (pos) {
printf("找到了\n");
printf("姓名:%s 性别:%s 年龄:%d 电话:%s 地址:%s\n",
pos->data.name,
pos->data.sex,
pos->data.age,
pos->data.tel,
pos->data.addr);
}
else {
printf("该联系人不存在\n");
}
}
6. 修改通讯录数据
void ModifyContact(contact** con) {
char name[NAME_MAX];
printf("请输入要修改的姓名:");
scanf("%s", name);
contact* pos = FindName(*con, name);
if (pos) {
printf("请输入修改的姓名:");
scanf("%s", pos->data.name);
printf("请输入修改的性别:");
scanf("%s", pos->data.sex);
printf("请输入修改的年龄:");
scanf("%d", &pos->data.age);
printf("请输入修改的电话:");
scanf("%s", pos->data.tel);
printf("请输入修改的地址:");
scanf("%s", pos->data.addr);
}
else {
printf("该联系人不存在,无法修改\n");
}
}
7. 销毁通讯录数据
void SaveContact(contact* con) {
assert(con);
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL) {
perror("fopen fail\n");
return;
}
while (con) {
fwrite(&(con->data), sizeof(con->data), 1, pf);
con = con->next;
}
printf("通讯录保存成功\n");
fclose(pf);
}
//销毁通讯录数据
void DestroyContact(contact** con) {
SaveContact(*con);
//销毁通讯录数据
SListDesTroy(con);
}
2. SList.c
创建节点
SLTNode* BuyNode(SLTDataType x) {
SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
//如果创建失败
if (node == NULL) {
perror("malloc fail!\n");
exit(1);
}
node->data = x;
node->next = NULL;
return node;//返回该节点
}
1. 尾插
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x) {
assert(pphead);//pphead不为空
SLTNode* node = BuyNode(x);//创建新节点
//如果链表为空
if (*pphead == NULL) {
*pphead = node;//新节点为头节点
return;
}
//找尾
SLTNode* cur = *pphead;
while (cur->next) {
cur = cur->next;
}
cur->next = node;//将新节点进行尾插
}
2. 删除指定节点
//删除pos
void SLTErase(SLTNode** pphead, SLTNode* pos) {
assert(pphead);
assert(pos);//检测pos节点不为空,如果pos节点不为空,那么该链表也不为空
//如果pos==头节点
if (*pphead == pos) {
// SLTNode *del = *pphead;
// *pphead = (*pphead)->next;
// free(del);
// del = NULL;
SLTPopFront(pphead);//头删
return;
}
//找pos的前一个节点
SLTNode* prev = *pphead;
while (prev->next) {
if (prev->next == pos) {
break;
}
prev = prev->next;
}
prev->next = pos->next;//删除pos节点
free(pos);//释放pos节点
// pos = NULL;//没有存在的必要
}
3. 销毁链表
void SListDesTroy(SLTNode** pphead) {
SLTNode* pointer = NULL;
while (*pphead) {
pointer = *pphead;
*pphead = (*pphead)->next;
free(pointer);
pointer = NULL;
}
}
4. 完整代码展示
1. contach.h
#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
//初始化通讯录
void InitContact(contact** con);
//添加通讯录数据
void AddContact(contact** con);
//删除通讯录数据
void DelContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);
2. SList.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "contach.h"
typedef PeoInfo SLTDataType;
struct SListNode
{
SLTDataType data;
struct SListNode* next;
};
typedef struct SListNode SLTNode;
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);
3. contach.c
#include "contach.h"
#include "SList.h"
//将contact.text文件中的数据导入到通讯录链表中
void LoadContact(contact** con) {
FILE* pf = fopen("contact.txt", "rb");//打开一个存放通讯录的二进制文件,只读
if (pf == NULL) {
perror("文件打开失败");
return;
}
PeoInfo ps;
//循环读取文件数据
while (fread(&ps, sizeof(ps), 1, pf)) {
SLTPushBack(con, ps);
}
printf("历史数据导入通讯录成功\n");
fclose(pf);//关闭文件
}
//初始化通讯录
void InitContact(contact** con) {
LoadContact(con);
printf("通讯录初始化完成\n");
}
//展示通讯录数据
void ShowContact(contact* con) {
while (con) {
printf("姓名:%s 性别:%s 年龄:%d 电话:%s 地址:%s\n",
con->data.name,
con->data.sex,
con->data.age,
con->data.tel,
con->data.addr);
con = con->next;
}
}
//添加通讯录数据(尾插)
void AddContact(contact** con) {
PeoInfo ps;
printf("请输入姓名:");
scanf("%s", ps.name);
printf("请输入性别:");
scanf("%s", ps.sex);
printf("请输入年龄:");
scanf("%d", &ps.age);
printf("请输入电话:");
scanf("%s", ps.tel);
printf("请输入地址:");
scanf("%s", ps.addr);
SLTPushBack(con, ps);//因为SLTPushBack的第一个参数是 contact** con
}
//查找通讯录中名字相同的数据
contact* FindName(contact* con, char name[]) {
assert(con);
while (con) {
if (strcmp(con->data.name, name) == 0) {
return con;
}
con = con->next;
}
return NULL;
}
//删除通讯录数据
void DelContact(contact** con) {
char name[NAME_MAX];
printf("输入要删除的姓名:");
scanf("%s", name);
contact* pos = FindName(*con, name);
if (pos) {
SLTErase(con, pos);
printf("删除成功\n");
}
else {
printf("该联系人不存在,无法删除\n");
}
}
//查找通讯录数据
void FindContact(contact* con) {
char name[NAME_MAX];
printf("输入要查找的姓名:");
scanf("%s", name);
contact* pos=FindName(con, name);
if (pos) {
printf("找到了\n");
printf("姓名:%s 性别:%s 年龄:%d 电话:%s 地址:%s\n",
pos->data.name,
pos->data.sex,
pos->data.age,
pos->data.tel,
pos->data.addr);
}
else {
printf("该联系人不存在\n");
}
}
//修改通讯录数据
void ModifyContact(contact** con) {
char name[NAME_MAX];
printf("请输入要修改的姓名:");
scanf("%s", name);
contact* pos = FindName(*con, name);
if (pos) {
printf("请输入修改的姓名:");
scanf("%s", pos->data.name);
printf("请输入修改的性别:");
scanf("%s", pos->data.sex);
printf("请输入修改的年龄:");
scanf("%d", &pos->data.age);
printf("请输入修改的电话:");
scanf("%s", pos->data.tel);
printf("请输入修改的地址:");
scanf("%s", pos->data.addr);
}
else {
printf("该联系人不存在,无法修改\n");
}
}
//将通讯录保存到contact.txt文件中
void SaveContact(contact* con) {
assert(con);
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL) {
perror("fopen fail\n");
return;
}
while (con) {
fwrite(&(con->data), sizeof(con->data), 1, pf);
con = con->next;
}
printf("通讯录保存成功\n");
fclose(pf);
}
//销毁通讯录数据
void DestroyContact(contact** con) {
SaveContact(*con);
//销毁通讯录数据
SListDesTroy(con);
}
4. SList.c
//创建节点
SLTNode* BuyNode(SLTDataType x) {
SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
//如果创建失败
if (node == NULL) {
perror("malloc fail!\n");
exit(1);
}
node->data = x;
node->next = NULL;
return node;//返回该节点
}
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x) {
assert(pphead);//pphead不为空
SLTNode* node = BuyNode(x);//创建新节点
//如果链表为空
if (*pphead == NULL) {
*pphead = node;//新节点为头节点
return;
}
//找尾
SLTNode* cur = *pphead;
while (cur->next) {
cur = cur->next;
}
cur->next = node;//将新节点进行尾插
}
// 删除pos
void SLTErase(SLTNode** pphead, SLTNode* pos) {
assert(pphead);
assert(pos);//检测pos节点不为空,如果pos节点不为空,那么该链表也不为空
//如果pos==头节点
if (*pphead == pos) {
// SLTNode *del = *pphead;
// *pphead = (*pphead)->next;
// free(del);
// del = NULL;
SLTPopFront(pphead);//头删
return;
}
//找pos的前一个节点
SLTNode* prev = *pphead;
while (prev->next) {
if (prev->next == pos) {
break;
}
prev = prev->next;
}
prev->next = pos->next;//删除pos节点
free(pos);//释放pos节点
// pos = NULL;//没有存在的必要
}
//销毁链表
void SListDesTroy(SLTNode** pphead) {
SLTNode* pointer = NULL;
while (*pphead) {
pointer = *pphead;
*pphead = (*pphead)->next;
free(pointer);
pointer = NULL;
}
}
5. text.c
#include "contach.h"
#include "SList.h"
void Realize(void) {
contact* con = NULL;
//初始化通讯录
InitContact(con);
//添加通讯录数据
AddContact(&con);
AddContact(&con);
AddContact(&con);
//删除通讯录数据
DelContact(&con);
//查找通讯录数据
FindContact(con);
//修改通讯录数据
ModifyContact(&con);
//展示通讯录数据
ShowContact(con);
//销毁通讯录数据
DestroyContact(&con);
}
int main() {
Realize();
return 0;
}