【创作赢红包】C语言实现建立手机通讯录(链式结构)

简介: 【创作赢红包】C语言实现建立手机通讯录(链式结构)

一.题目要求

今天来和大家分享一个简易通讯录(C语言实现)


首先要介绍一下通讯录的基本功能


添加联系人信息

删除指定联系人信息

查找指定联系人信息

修改指定联系人信息

显示所有联系人信息

二.思路分析

1. 首先需要定义通讯录的数据结构,可以使用结构体来表示每个联系人的信息,包括姓名、电话号码、地址等。


2. 接着需要定义一个数组来存储所有联系人的信息,数组的大小可以根据实际需求进行调整。


3. 编写程序菜单,包括添加联系人、删除联系人、查找联系人、显示所有联系人等功能。


4. 在添加联系人功能中,需要让用户输入联系人的信息,并将其存储到数组中。


5. 在删除联系人功能中,需要让用户输入要删除的联系人姓名,并在数组中查找并删除该联系人的信息。


6. 在查找联系人功能中,需要让用户输入要查找的联系人姓名,并在数组中查找并显示该联系人的信息。


7. 在显示所有联系人功能中,需要遍历数组并逐个显示每个联系人的信息。


8. 最后,可以使用文件读写功能将通讯录数据保存到文件中,以便下次启动程序时可以读取之前保存的数据。

本次实验是上次实验的补充,可以参考。


三.各部分功能实现

1.定义通讯录结构

       在单链表中,假设每个结点的类型用LinkNode表示,它应包括存储元素的数据域,这里用data表示,其类型用通用类型标识符ElemType 表示,还包括存储后继结点位置的指针域,这里用next表示。LinkNode 类型的声明如下:

//定义通讯录结构表
typedef struct {
  char name[20];
  char tel[15];
  char place[20];
} Elemtype;
//声明链表
typedef struct LNode
{
  Elemtype data;
  struct LNode *next;
}LinkNode;
/*
  本次实验是用单链表来实现通讯录的建立
  这里我们的基本思路是先初始化一个空的线性表,然后通过读入数组的信息来建立初始的通讯录。
  读入数组的信息有两种方法读取插入:头插法和尾插法。
  头插法:将读取的数组元素存放在新节点的数据域中,然后将其插入到当前链表的表头(头节点之后)
  尾插法:将读取的数组元素存放在新节点的数据域中,然后将其插入到当前链表的表尾(尾节点之后)
  这里分别用两种方法进行开始信息的插入。
 */

2.初始化以及销毁线性表

初始化:建立一个空的 单链表

销毁线性表:该运算释放单链表L占用的内存空间,即逐一释放全部结点的空间。其过程是让pre、p 指向两个相邻的结点(初始时pre指向头结点,p指向首结点)。当p不为空时循环:释放结点pre,然后pre、p同步后移一个结点。循环结束后,pre指向尾结点,再将其释放。算法如下:

//初始化线性表,即建立一个空的链表
void InitList(LinkNode *&L)
{
  L=(LinkNode * )malloc(sizeof(LinkNode));
  L->next=NULL;                  //创建头节点,其next域置为空
}
/*
     头插法算法思路:
      1.首先建立一个头节点L(已存在);
      2.读入数组
      3.建立一个新节点p
      4.将数组元素依次赋值给p的数据域
      5.修改指针
      6.重复3~5,直到结束
*/
//头插法建立单链表
void CreateListF(LinkNode *&L,Elemtype a[],int n)
{
  LinkNode *s;
  //头节点已存在,不再在这里建立了
  for(int i=0;i<n;i++)
  {
    s=(LinkNode * )malloc(sizeof(LinkNode));   //创建数据新节点
    s->data=a[i];                              //将数组元素赋值给s的数据域
    s->next=L->next;                          //将s放在原来L节点之后
    L->next=s;
  }
}
//尾插法建立单链表
void CreateListR(LinkNode *&L,Elemtype a[],int n)
{
  LinkNode *s,*r;
  //头节点已存在,不再在这里建立了
  r=L;                       //r始终指向尾节点,但初始时指向头节点(初始时头节点即为尾节点)
  for(int i=0;i<n;i++)
  {
    s=(LinkNode * )malloc(sizeof(LinkNode));  //创建数据新节点
    s->data=a[i];                            //将数组元素赋值给新节点s的数据域
    r->next=s;                               //将s放在原来尾指针r的后面
    r=s;
  }
  r->next=NULL;                              //插入完成后,尾节点的next域为空
}

3.插入数据元素

  • 该运算的实现过程是先在单链表L中找到第i-1个结点,由p指向它。若存在这样的
    结点,将值为e的结点(s指向它)插人到p所指结点的后面。算法如下:
//插入数据元素
bool ListInsert(LinkNode *&L,int i,Elemtype e)
{
  /*在链表L的第i个位置上插入新元素e*/
  int j=0;
  LinkNode *p=L,*s;      //p开始指向头节点,s为存放数据新节点
  if(i<=0)               //位置不对就报错
    return false;
  while(j<i-1 && p!=NULL)       //定位,使p指向第i-1个节点
  {
    j++;
    p=p->next;
  }
  if(p==NULL)                 //如果没找到第i-1个节点就报错
    return false;
  else                        //成功定位后,执行下面操作
  {
    s=(LinkNode * )malloc(sizeof(LinkNode));          //创建新节点s,其数据域置为e
    s->data=e;
    s->next=p->next;                                //创建的新节点s放在节点p之后
    p->next=s;
    return true;
  }
}

4.删除数据元素

  • 该运算的实现过程是先在单链表L中找到第i-1个结点,由p指向它。若存在这样结点,且也存在后继结点(由q指向它),则删除q所指的结点,返回true;否则返回false表示参数i错误。算法如下:
//删除数据元素
bool ListDelate (LinkNode *&L, int i, Elemtype &e)
{
  /*删除链表中的第i个元素,其值为e*/
  int j=0;
  LinkNode *p=L,*q;               //p开始指向头节点,q为要删除的节点
  if(i<0)                         //位置不对就报错
    return false;
  while(j<i-1 && p!=NULL)        //定位,使p指向第i-1个节点
  {
    j++;
    p=p->next;
  }
  if(p==NULL)                    //如果没找到第i-1个节点就报错
    return false;
  else                           //成功定位后,执行下面操作
  {
    q=p->next;                   //q指向第i个节点
    if(q==NULL)                 //如果不存在要删除的节点就报错
      return false;
    e=q->data;                 //e存放要删除节点的数据域
    p->next=q->next;           //从单链表中删除q节点
    free(q);                   //释放q节点
    return true;
  }
}

5.输出线性表

  • 该运算逐一扫描单链表L的每个数据结点,并显示各结点的data域值。
//输出线性表
void DispList(LinkNode *L)
{
  LinkNode *p=L->next;                   //p指向首节点
  printf("------------通讯录-------------\n");
  while(p!=NULL)                         //p不为空就输出p节点的data域
  {
    printf("%s    %s    %s\n",p->data.name,p->data.tel,p->data.place);
    p=p->next;                         //p移向下一位节点
  }
  printf("-------------------------------\n");
}

6.求通讯录表长度

  • 该运算返回单链表L中数据结点的个数。由于单链表没有存放数据结点个数的信息,
    需要通过遍历来统计。其过程是让p指向头结点,n用来累计数据结点个数(初始值为0),
    当p不为空时循环: n增1,p指向下一个结点。循环结束后返回n。算法如下:
//求通讯录表长度
int ListLength(LinkNode *L)
{
  int count=0;                    //统计长度,开始为0
  LinkNode *p=L;                  //p指向头节点,此时计数器为0
  while(p->next!=NULL)            //开始计数,p移动一次计数器加1
  {
    count++;
    p=p->next;
  }
  return count;                 //循环结束,p指向尾节点,count为节点个数
}

7.获取数据元素

该运算在单链表L中从头开始找到第i个结点,若存在第i个数据结点,则将其date值赋给变量e。其过程是让p指向头结点,j用来累计遍历过的数据结点个数(初始值为0),当j<i且p不为空时循环;j增1,p指向下一个结点。循环结束后有两种情况,若p为空,表示单链表 L中没有第i 个数据结点(参数i错误),返 回false; 否则找到第i个数据与点,提取它的值并返回true.。算法如下:

//获取线性表中某个位置数据元素
bool GetElem(LinkNode *L,int i,Elemtype &e)
{
  int j=0;
  LinkNode *p=L;                  //p开始指向头节点
  if(i<0)                         //位置不对就报错
    return false;
  while(j<i && p!=NULL)        //定位,使p指向第i个节点
  {
    j++;
    p=p->next;
  }
  if(p==NULL)                    //如果没找到第i个节点就报错
    return false;
  else                              //成功定位后,执行下面操作
  {
    e=p->data;                   //e存放第i个节点的数据域
    return true;
  }
}

8.元素查找

  • 该运算在单链表工中从头开始找第一个值域与e相等的结点,若存在这样的结点,则返
    回逻辑序号,否则返回0。算法如下:


//元素查找(通讯录姓名查找)
int LocateElem(LinkNode *L, Elemtype e)
{
  /*从头开始查找第一个值域与e相等的节点,返回逻辑序号(根据e找i)*/
  int i=1;                    
  LinkNode *p=L->next;        //p指向首节点,首节点的序号为1
  while(p!=NULL && strcmp(p->data.name,e.name))         //循环查找姓名相同的节点
  {
    p=p->next;
    i++;  
  }
  if(p==NULL)                               //不存在这样的节点返回0
    return 0;
  else
    printf("%s   %s   %s\n",p->data.name,p->data.tel,p->data.place);
    return i;
}

四. 完整代码

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//定义通讯录结构表
typedef struct {
  char name[20];
  char tel[15];
  char place[20];
} Elemtype;
//声明链表
typedef struct LNode
{
  Elemtype data;
  struct LNode *next;
}LinkNode;
/*
  本次实验是用单链表来实现通讯录的建立
  这里我们的基本思路是先初始化一个空的线性表,然后通过读入数组的信息来建立初始的通讯录。
  读入数组的信息有两种方法读取插入:头插法和尾插法。
  头插法:将读取的数组元素存放在新节点的数据域中,然后将其插入到当前链表的表头(头节点之后)
  尾插法:将读取的数组元素存放在新节点的数据域中,然后将其插入到当前链表的表尾(尾节点之后)
  这里分别用两种方法进行开始信息的插入。
 */
//初始化线性表,即建立一个空的链表
void InitList(LinkNode *&L)
{
  L=(LinkNode * )malloc(sizeof(LinkNode));
  L->next=NULL;                  //创建头节点,其next域置为空
}
/*
     头插法算法思路:
      1.首先建立一个头节点L(已存在);
      2.读入数组
      3.建立一个新节点p
      4.将数组元素依次赋值给p的数据域
      5.修改指针
      6.重复3~5,直到结束
*/
//头插法建立单链表
void CreateListF(LinkNode *&L,Elemtype a[],int n)
{
  LinkNode *s;
  //头节点已存在,不再在这里建立了
  for(int i=0;i<n;i++)
  {
    s=(LinkNode * )malloc(sizeof(LinkNode));   //创建数据新节点
    s->data=a[i];                              //将数组元素赋值给s的数据域
    s->next=L->next;                          //将s放在原来L节点之后
    L->next=s;
  }
}
//尾插法建立单链表
void CreateListR(LinkNode *&L,Elemtype a[],int n)
{
  LinkNode *s,*r;
  //头节点已存在,不再在这里建立了
  r=L;                       //r始终指向尾节点,但初始时指向头节点(初始时头节点即为尾节点)
  for(int i=0;i<n;i++)
  {
    s=(LinkNode * )malloc(sizeof(LinkNode));  //创建数据新节点
    s->data=a[i];                            //将数组元素赋值给新节点s的数据域
    r->next=s;                               //将s放在原来尾指针r的后面
    r=s;
  }
  r->next=NULL;                              //插入完成后,尾节点的next域为空
}
//插入数据元素
bool ListInsert(LinkNode *&L,int i,Elemtype e)
{
  /*在链表L的第i个位置上插入新元素e*/
  int j=0;
  LinkNode *p=L,*s;      //p开始指向头节点,s为存放数据新节点
  if(i<=0)               //位置不对就报错
    return false;
  while(j<i-1 && p!=NULL)       //定位,使p指向第i-1个节点
  {
    j++;
    p=p->next;
  }
  if(p==NULL)                 //如果没找到第i-1个节点就报错
    return false;
  else                        //成功定位后,执行下面操作
  {
    s=(LinkNode * )malloc(sizeof(LinkNode));          //创建新节点s,其数据域置为e
    s->data=e;
    s->next=p->next;                                //创建的新节点s放在节点p之后
    p->next=s;
    return true;
  }
}
//删除数据元素
bool ListDelate (LinkNode *&L, int i, Elemtype &e)
{
  /*删除链表中的第i个元素,其值为e*/
  int j=0;
  LinkNode *p=L,*q;               //p开始指向头节点,q为要删除的节点
  if(i<0)                         //位置不对就报错
    return false;
  while(j<i-1 && p!=NULL)        //定位,使p指向第i-1个节点
  {
    j++;
    p=p->next;
  }
  if(p==NULL)                    //如果没找到第i-1个节点就报错
    return false;
  else                           //成功定位后,执行下面操作
  {
    q=p->next;                   //q指向第i个节点
    if(q==NULL)                 //如果不存在要删除的节点就报错
      return false;
    e=q->data;                 //e存放要删除节点的数据域
    p->next=q->next;           //从单链表中删除q节点
    free(q);                   //释放q节点
    return true;
  }
}
//输出线性表
void DispList(LinkNode *L)
{
  LinkNode *p=L->next;                   //p指向首节点
  printf("------------通讯录-------------\n");
  while(p!=NULL)                         //p不为空就输出p节点的data域
  {
    printf("%s    %s    %s\n",p->data.name,p->data.tel,p->data.place);
    p=p->next;                         //p移向下一位节点
  }
  printf("-------------------------------\n");
}
//求通讯录表长度
int ListLength(LinkNode *L)
{
  int count=0;                    //统计长度,开始为0
  LinkNode *p=L;                  //p指向头节点,此时计数器为0
  while(p->next!=NULL)            //开始计数,p移动一次计数器加1
  {
    count++;
    p=p->next;
  }
  return count;                 //循环结束,p指向尾节点,count为节点个数
}
//获取线性表中某个位置数据元素
bool GetElem(LinkNode *L,int i,Elemtype &e)
{
  int j=0;
  LinkNode *p=L;                  //p开始指向头节点
  if(i<0)                         //位置不对就报错
    return false;
  while(j<i && p!=NULL)        //定位,使p指向第i个节点
  {
    j++;
    p=p->next;
  }
  if(p==NULL)                    //如果没找到第i个节点就报错
    return false;
  else                              //成功定位后,执行下面操作
  {
    e=p->data;                   //e存放第i个节点的数据域
    return true;
  }
}
//元素查找(通讯录姓名查找)
int LocateElem(LinkNode *L, Elemtype e)
{
  /*从头开始查找第一个值域与e相等的节点,返回逻辑序号(根据e找i)*/
  int i=1;                    
  LinkNode *p=L->next;        //p指向首节点,首节点的序号为1
  while(p!=NULL && strcmp(p->data.name,e.name))         //循环查找姓名相同的节点
  {
    p=p->next;
    i++;  
  }
  if(p==NULL)                               //不存在这样的节点返回0
    return 0;
  else
    printf("%s   %s   %s\n",p->data.name,p->data.tel,p->data.place);
    return i;
}
//判断通讯录为空
bool ListEmpty(LinkNode *L)
{
  return (L->next==NULL);
}
//菜单实现
void menu() {
  printf("   -------------------------------\n");
  printf("   通讯录的应用:\n");
  printf("   -------------------------------\n");
  printf("      1.建立(初始化)通讯录\n");
  printf("      2.显示联系人信息\n");
  printf("      3.增加联系人信息\n");
  printf("      4.删除联系人信息\n");
  printf("      5.查找联系人信息\n");
  printf("      6.退出程序!!!\n");
  printf("   -------------------------------\n");
}
int main() 
{
  LinkNode *L;
  int flag = 1;   //定义循环体条件
  int i, j;         //存放用户输入的选项
  Elemtype a[4] = {"张三", "15671580583", "北京","李四", "13387592396","上海","王五", "15994272725","武汉", "赵六", "15972200598","昆明"};
  Elemtype e;
  menu();
  printf("初始化顺序表并用头插法插入开始元素:\n");
  InitList(L);         //这时是一个空表,接下来通过头插法创建线性表完成初始化
  CreateListF(L,a,4);  //采用头插法建立完成
  DispList(L);         //展示初始化成果
  while(flag==1)
  {
    printf("请输入你的选择:\n");
    scanf("%d", &j);
    switch(j)
    {
    case 1:
      printf("已经完成初始化\n");
        break;
    case 2:
      DispList(L);
      break;
    case 3:
      printf("请输入联系人姓名、电话与地址:");
      scanf("%s   %s   %s", e.name, e.tel,e.place);
      printf("请输入插入数据的位置:");
      scanf("%d", &i);
      printf("\n");
      ListInsert(L,i,e);
      break;
    case 4:
      printf("请输入删除数据的位置:");
      scanf("%d", &i);
      ListDelate(L, i, e);
      break;
    case 5:
      printf("请输入联系人姓名:");
      scanf("%s", &e.name);
      LocateElem(L, e);
      break;
    case 6:
      flag = 0;
      printf("退出程序\n");
      break;
    }
  }
}

五.运行截图


e843a40933ca4199a7996660c40e15fc.png

六.补充

可以去上一篇文章(顺序结构)查看不同方式:顺序存储方式


相关文章
|
2天前
|
C语言
四:C语言-选择结构
本篇文章详细讲述了C语言中的选择结构(if...else语句和switch语句)并附上多个代码示例
17 9
|
10天前
|
机器学习/深度学习 搜索推荐 程序员
C语言实现个人通讯录(功能优化)-2
C语言实现个人通讯录(功能优化)
C语言实现个人通讯录(功能优化)-2
|
10天前
|
存储 C语言 索引
C语言实现个人通讯录(功能优化)-1
C语言实现个人通讯录(功能优化)
C语言实现个人通讯录(功能优化)-1
|
17天前
|
C语言
C语言学习记录——通讯录(静态内存)
C语言学习记录——通讯录(静态内存)
14 2
|
18天前
|
编译器 C语言
C语言深度理解之——结构体内存对齐
C语言深度理解之——结构体内存对齐
16 1
|
23天前
|
编译器 C语言
C语言中的选择结构详解与实现
C语言中的选择结构详解与实现
27 1
|
23天前
|
存储 算法 数据处理
C语言中的顺序结构程序
C语言中的顺序结构程序
18 1
|
28天前
|
Android开发
【苹果安卓通用】xlsx 和 vCard 文件转换器,txt转vCard文件格式,CSV转 vCard格式,如何批量号码导入手机通讯录,一篇文章说全
本文介绍了如何快速将批量号码导入手机通讯录,适用于企业客户管理、营销团队、活动组织、团队协作和新员工入职等场景。步骤包括:1) 下载软件,提供腾讯云盘和百度网盘链接;2) 打开软件,复制粘贴号码并进行加载预览和制作文件;3) 将制作好的文件通过QQ或微信发送至手机,然后按苹果、安卓或鸿蒙系统的指示导入。整个过程简便快捷,可在1分钟内完成。
|
27天前
|
存储 C语言
C语言实现通讯录
C语言实现通讯录
21 2
|
27天前
|
存储 C语言
C语言实验-动态顺序表实现简易通讯录(二)
在这个C语言实验中,你将实现一个简单的通讯录,它使用动态顺序表来存储联系人信息。
28 2