【创作赢红包】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

六.补充

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


相关文章
|
存储 安全 C语言
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
600 10
|
小程序 C语言
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
458 10
|
存储 编译器 C语言
【C语言程序设计——选择结构程序设计】求一元二次方程的根(头歌实践教学平台习题)【合集】
本任务要求根据求根公式计算并输出一元二次方程的两个实根,精确到小数点后两位。若方程无实根,则输出提示信息。主要内容包括: - **任务描述**:使用求根公式计算一元二次方程的实根。 - **相关知识**:掌握 `sqrt()` 函数的基本使用方法,判断方程是否有实根。 - **编程要求**:根据输入的系数,计算并输出方程的根或提示无实根。 - **测试说明**:提供两组测试数据及预期输出,确保代码正确性。 - **通关代码**:包含完整的 C 语言代码示例,实现上述功能。 通过本任务,你将学会如何处理一元二次方程的求解问题,并熟悉 `sqrt()` 函数的使用。
401 5
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】按从小到大排序三个数(头歌实践教学平台习题)【合集】
本任务要求从键盘输入三个数,并按从小到大的顺序排序后输出。主要内容包括: - **任务描述**:实现三个数的排序并输出。 - **编程要求**:根据提示在编辑器中补充代码。 - **相关知识**: - 选择结构(if、if-else、switch) - 主要语句类型(条件语句) - 比较操作与交换操作 - **测试说明**:提供两组测试数据及预期输出。 - **通关代码**:完整代码示例。 - **测试结果**:展示测试通过的结果。 通过本任务,你将掌握基本的选择结构和排序算法的应用。祝你成功!
362 4
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】求阶跃函数的值(头歌实践教学平台习题)【合集】
本任务要求输入x的值,计算并输出特定阶跃函数的结果。主要内容包括: 1. **选择结构基本概念**:介绍if、if-else、switch语句。 2. **主要语句类型**:详细解释if、if-else、switch语句的使用方法。 3. **跃迁函数中变量的取值范围**:说明如何根据条件判断变量范围。 4. **计算阶跃函数的值**:通过示例展示如何根据给定条件计算函数值。 编程要求:在右侧编辑器Begin-End之间补充代码,实现阶跃函数的计算和输出。测试说明提供了多个输入及其预期输出,确保代码正确性。最后提供通关代码和测试结果,帮助理解整个过程。
388 0
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】判断一个数是不是5和7的倍数(头歌实践教学平台习题)【合集】
本任务要求输入一个正整数,判断其是否同时是5和7的倍数,若是输出&quot;Yes&quot;,否则输出&quot;No&quot;。内容涵盖选择结构的基本概念、主要语句类型(if、if-else、switch)及条件判断逻辑,帮助理解编程中的分支执行与条件表达式。测试用例包括正数、负数及非倍数情况,确保代码逻辑严谨。通关代码示例如下: ```cpp #include &quot;stdio.h&quot; int main(){ int a; scanf(&quot;%d&quot;, &a); if (a &lt;= 0){ printf(&quo
761 0
|
编译器 C语言 C++
【C语言程序设计——选择结构程序设计】求输入的日期是该年的第几天(头歌实践教学平台习题)【合集】
本任务要求编写程序,根据用户输入的年月日(以空格或回车分隔),计算并输出该天是该年的第几天,需注意判断闰年。主要内容包括: 1. **任务描述**:实现从键盘输入年月日,计算该天是当年的第几天。 2. **相关知识**: - `switch` 结构的基本语法及使用注意事项。 - 判断闰年的条件:能被4整除但不能被100整除,或能被400整除的年份为闰年。 3. **编程要求**:根据提示补充代码,确保程序正确处理输入并输出结果。 4. **测试说 示例代码展示了如何使用 `switch` 语句和闰年判断逻辑来完成任务。通过此练习,掌握 `switch` 语句的应用及闰年判断方法。
695 0
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
903 16
|
编译器 C语言 Python
C语言结构
C语言结构
160 0
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
304 2

热门文章

最新文章