【创作赢红包】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月前
|
网络协议 编译器 Linux
【C语言】结构体内存对齐:热门面试话题
【C语言】结构体内存对齐:热门面试话题
108 0
|
1月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
87 16
|
2月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
37 2
|
2月前
|
C语言
链式顺序表实现(C语言描述)
本文介绍了如何在C语言中实现链式顺序表,包括数据结构的定义、节点的创建、数据的插入和删除以及链表的打印和销毁。
49 2
|
2月前
|
编译器 C语言 Python
C语言结构
C语言结构
21 0
|
3月前
|
存储 编译器 程序员
C语言程序的基本结构
C语言程序的基本结构包括:1)预处理指令,如 `#include` 和 `#define`;2)主函数 `main()`,程序从这里开始执行;3)函数声明与定义,执行特定任务的代码块;4)变量声明与初始化,用于存储数据;5)语句和表达式,构成程序基本执行单位;6)注释,解释代码功能。示例代码展示了这些组成部分的应用。
135 10
|
3月前
|
C语言
C语言程序设计核心详解 第四章&&第五章 选择结构程序设计&&循环结构程序设计
本章节介绍了C语言中的选择结构,包括关系表达式、逻辑表达式及其运算符的优先级,并通过示例详细解释了 `if` 语句的不同形式和 `switch` 语句的使用方法。此外,还概述了循环结构,包括 `while`、`do-while` 和 `for` 循环,并解释了 `break` 和 `continue` 控制语句的功能。最后,提供了两道例题以加深理解。
106 7
|
2月前
|
存储 C语言
手把手教你用C语言实现通讯录管理系统
手把手教你用C语言实现通讯录管理系统
|
3月前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
3月前
|
C语言
C语言程序设计核心详解 第三章:顺序结构,printf(),scanf()详解
本章介绍顺序结构的基本框架及C语言的标准输入输出。程序从`main()`开始依次执行,框架包括输入、计算和输出三部分。重点讲解了`printf()`与`scanf()`函数:`printf()`用于格式化输出,支持多种占位符;`scanf()`用于格式化输入,需注意普通字符与占位符的区别。此外还介绍了`putchar()`和`getchar()`函数,分别用于输出和接收单个字符。