- 前言
- 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 50typedef 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;
}
后言
以上就是小编对单链表应用的一些初步认识。
如果觉得小编讲的还可以,还请一键三连。互三必回!
持续更新中!