当前位置:   article > 正文

数据结构:用顺序表和单链表实现通讯录(上)_数据结构链表实现通讯录

数据结构链表实现通讯录

前言

首先简要介绍顺序表和链表的概念和区别以作区分。

顺序表:逻辑上是线性的,物理性质上也是线性的。逻辑是线性的(连续的)体现在它可以通过第一个数找到接下来的数。物理性质上的线性体现在分配给它的内存是连续的。它本质上就像一个数组,可以通过下标来访问成员。

单链表:这里说的单链表是指不带头单向不循环链表。链表和顺序表是不同的。链表在逻辑上是线性的,但在物理性质上是非线性的。需要的时候申请一块内存,但这块内存和其他内存不一定连续,它可能是散落在四周的,只是通过记住下一个节点的位置,使这些单独存在的空间有了联系。它们在逻辑上就像一条串好的线。一个结点接一个结点。就像火车一样,需要的时候装个车厢,不需要就把车厢撤下来。

顺序表和链表的对比

顺序表:

缺点:

1.申请空间一般按2的倍数申请,但是在使用的时候可能只需要用几个,因此会造成一定程度的空间浪费。

2.每次删除一个数,就要移动这个数后面的其他所有数。删除的数剧靠前,数据总量庞大时,效率太低了。

3.每次内存满了以后就要扩容(realloc),扩容导致性能效率低。

优点:

1.释放内存可以一次性释放。方便。

2.知道一个数的下标,就能访问相邻的两个数。

链表:

缺点:

1.销毁链表时,释放申请的内存属于申请了多少块就要释放多少。

2.单链表只能记住下一个结点的地址,没办法通过当前的地址找到上一个。找上一个结点时要一个个去找。

优点:

1.需要添加一个结点就申请一块空间。不会造成空间浪费。

2.每次删除一个数,只需要改动这个数相邻的两个结点的指针,相对来说影响很小。

3.单链表是申请(malloc)新的空间,不存在扩容。

在实践中,单链表和顺序表都会用,主要看应用场景是什么。

顺序表实现通讯录

1.分析

通讯录的功能有添加联系人,删除联系人,查找联系人,修改联系人,展示通讯录,退出通讯录六个功能。这次需要实现的就六个功能。

建立三个文件。sqcontact.c、sqcontact.h、test.c

sqcontact.h主要放头文件声明,函数声明,宏等。

sqcontact.c主要写函数的具体实现,函数的定义。

test.c主要写通讯录功能的测试,属于测试文件。

2.先写头文件,sqcontact.h

用注释说明了。

头文件里主要写你想要实现哪些功能,就声明哪些函数。先写的时候可以不完整,后面想到再加。

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #pragma warning(disable:6031)
  3. #pragma once//防止被重复引用
  4. #define NAME_MAX 100
  5. #define SEX_MAX 10
  6. #define TEL_MAX 14
  7. #define ADDR_MAX 100
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <assert.h>
  11. #include <stdlib.h>
  12. typedef struct personinfo //联系人是一个结构体,这个结构体里面包含有五个信息。
  13. {
  14. char name[NAME_MAX];
  15. char sex[SEX_MAX];
  16. int age; //其他信息是数组,因为单个char内存太小不够写,但年龄只需要整型就够
  17. char tel[TEL_MAX];
  18. char addr[ADDR_MAX];
  19. }personinfo; //把struct personinfo重命名为personinfo
  20. typedef struct seqlist //整个通讯录是一个结构体,这个结构体里面有联系人数组
  21. {
  22. personinfo* info;//这是一个联系人数组,里面有各个联系人信息
  23. int capacity;//这个是通讯录的空间大小,可以装多少联系人
  24. int size; //这个是通讯录的实际大小,装了多少个联系人
  25. }contact;
  26. void Menu(); //菜单的声明 下面是各个功能实现的函数。
  27. //初始化通讯录
  28. void Initcontact(contact* con);//传一级指针是因为要改变con的内容,下面保持接口一致性
  29. //扩容
  30. void Creatcapacity(contact* con);
  31. //添加联系人
  32. void Addcontact(contact* con);
  33. //删除联系人
  34. void Delcontact(contact* con);
  35. //展示联系人
  36. void Showcontact(contact* con);
  37. //查找联系人
  38. void Findcontact(contact* con);
  39. //修改联系人
  40. void Modifycontact(contact* con);
  41. //销毁通讯录
  42. void Destorycontact(contact* con);

2.再写sqcontact.c文件

这里首先要包含头文件的内容。然后去实现头文件的那些函数。

写完一些功能的时候可以测试一下这些代码是否无误。

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #pragma warning(disable:6031)
  3. #include "sqcontact.h"
  4. void Menu()
  5. {
  6. printf("\n");
  7. printf("***************************\n");
  8. printf("1.添加联系人\n");
  9. printf("2.删除联系人\n");
  10. printf("3.修改联系人\n");
  11. printf("4.展示联系人\n");
  12. printf("5.查找联系人\n");
  13. printf("0.退出通讯录\n");
  14. printf("***************************\n\n");
  15. }
  16. //初始化通讯录
  17. void Initcontact(contact* con)
  18. {
  19. con->info = NULL; //初始化通讯录就是把里面的指针置为NULL,其他赋值。
  20. con->capacity = 0; //也可以把指针赋值,只是为了避免它成为野指针。
  21. con->size = 0;
  22. }
  23. //扩容
  24. void Creatcapacity(contact* con)
  25. {
  26. if (con->capacity == con->size)//如果容量和实际数据相等(包括0),就需要扩容。
  27. {
  28. int newcapacity = con->capacity == 0 ? 4 : (2 * con->capacity);
  29. personinfo* ptr = (personinfo*)realloc(con->info, sizeof(personinfo) * newcapacity);
  30. if (ptr == NULL)
  31. {
  32. perror("realloc failured");
  33. exit(1);
  34. }
  35. con->info = ptr;
  36. con->capacity = newcapacity;
  37. }//还有容量则什么都不做
  38. }
  39. //尾插
  40. void Addback(contact* con, personinfo* x)
  41. {
  42. //一种是直接尾插,一种是头插
  43. if (con->size==0) //头插,因为联系人里没有数据
  44. {
  45. Creatcapacity(con);//尾插的时候要看一下空间够不够
  46. con->info[0] = *x;
  47. con->size++;
  48. }
  49. else//尾插,联系人中有数据
  50. {
  51. Creatcapacity(con);
  52. con->info[con->size] = *x;
  53. con->size++;
  54. }
  55. }
  56. //添加联系人 包含扩容,尾插
  57. void Addcontact(contact* con)
  58. {
  59. personinfo x; //创建一个联系人结构体,填充内容,再把它传给尾插的函数。
  60. Creatcapacity(con); //这句可以不写,后面尾插的时候扩容了的
  61. printf("请输入您要添加的联系人姓名:\n");
  62. scanf("%s", x.name); //数组名是首元素地址,所以不用再取地址了
  63. printf("请输入您要添加的联系人性别:\n");
  64. scanf("%s", x.sex);
  65. printf("请输入您要添加的联系人年龄:\n");
  66. scanf("%d", &x.age); //注意,这里要取地址。这个是整型
  67. printf("请输入您要添加的联系人电话:\n");
  68. scanf("%s", x.tel);
  69. printf("请输入您要添加的联系人地址:\n");
  70. scanf("%s", x.addr);
  71. Addback(con, &x);
  72. printf("联系人添加成功!\n");
  73. Showcontact(con);//添加成功以后再展示一遍当前通讯录
  74. }
  75. //指定位置删除联系人
  76. void Del(contact* con, int pos)
  77. {
  78. for (int i = pos; i < con->size-1; i++)//i如果是最后一个,下标为size-1的数,那么
  79. {
  80. con->info[i] = con->info[i + 1];
  81. }
  82. con->size--;//通过size--,最后一个数也被删掉了,这个逻辑就完整了。(访问不了等于删掉
  83. }
  84. //删除联系人包含 找联系人 删除联系人
  85. void Delcontact(contact* con)
  86. {
  87. assert(con);
  88. printf("请输入您要删除的联系人:\n");
  89. char name[NAME_MAX];
  90. scanf("%s", name);
  91. for (int i = 0; i < con->size; i++)
  92. {
  93. if (strcmp(con->info[i].name, name) == 0)//找联系人
  94. {
  95. //任意位置删除联系人
  96. Del(con, i);
  97. printf("联系人删除成功\n\n");
  98. Showcontact(con);
  99. return;
  100. }
  101. }
  102. printf("该联系人不存在!\n");
  103. }
  104. //展示联系人
  105. void Showcontact(contact* con)
  106. {
  107. if (con == NULL)
  108. {
  109. return;
  110. }
  111. printf("当前联系人如下:\n");
  112. printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址");
  113. for (int i = 0; i < con->size; i++)
  114. {
  115. printf("%-4s ", con->info[i].name);
  116. printf("%-4s ", con->info[i].sex);
  117. printf("%-4d ", con->info[i].age);
  118. printf("%-4s ", con->info[i].tel);
  119. printf("%-4s ", con->info[i].addr);
  120. printf("\n");
  121. }
  122. }
  123. //查找联系人 这个代码和前面有很多重复部分,找联系人。直接复制之前的代码再修改一下
  124. void Findcontact(contact* con)
  125. {
  126. assert(con);
  127. printf("请输入您要查找的联系人:\n");
  128. char name[NAME_MAX];
  129. scanf("%s", name);
  130. for (int i = 0; i < con->size; i++)
  131. {
  132. if (strcmp(con->info[i].name, name) == 0)
  133. {
  134. printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址");
  135. printf("%-4s ", con->info[i].name);
  136. printf("%-4s ", con->info[i].sex);
  137. printf("%-4d ", con->info[i].age);
  138. printf("%-4s ", con->info[i].tel);
  139. printf("%-4s ", con->info[i].addr);
  140. printf("\n");
  141. return;
  142. }
  143. }
  144. printf("该联系人不存在!\n");
  145. }
  146. //修改联系人 找联系人,再改联系人。这个代码和前面代码有很多重复部分。直接复制过来再改改
  147. void Modifycontact(contact* con)
  148. {
  149. assert(con);
  150. printf("请输入您要修改的联系人:\n");
  151. char name[NAME_MAX];
  152. scanf("%s", name);
  153. for (int i = 0; i < con->size; i++)
  154. {
  155. if (strcmp(con->info[i].name, name) == 0)
  156. {
  157. printf("请输入您要修改的联系人姓名:\n");
  158. scanf("%s", con->info[i].name);
  159. printf("请输入您要修改的联系人性别:\n");
  160. scanf("%s", con->info[i].sex);
  161. printf("请输入您要修改的联系人年龄:\n");
  162. scanf("%d", &con->info[i].age);
  163. printf("请输入您要修改的联系人电话:\n");
  164. scanf("%s", con->info[i].tel);
  165. printf("请输入您要修改的联系人地址:\n");
  166. scanf("%s", con->info[i].addr);
  167. printf("联系人修改成功!\n");
  168. Showcontact(con);
  169. return;
  170. }
  171. }
  172. printf("该联系人不存在!\n");
  173. }
  174. //销毁通讯录
  175. void Destorycontact(contact* con)
  176. {
  177. assert(con); //不能释放已经释放的动态内存
  178. free(con->info); //释放内存空间
  179. con->info = NULL;//再把指针置为空
  180. con->size = con->capacity = 0;//其他变为0
  181. }

3.写test.c文件

可以先实现一部分功能以后就写test.c文件测试一下那些功能是否能顺利运行。

首先还是包含一个头文件。初始化和创建通讯录不能在do while循环里。测试文件相比比较简单。

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #pragma warning(disable:6031)
  3. #include "sqcontact.h"
  4. int main()
  5. {
  6. contact s1;
  7. Initcontact(&s1);
  8. int num = 0;
  9. do
  10. {
  11. Menu();
  12. printf("请输入您要进行的操作:\n");
  13. scanf("%d", &num);
  14. switch (num)
  15. {
  16. case 1: Addcontact(&s1);
  17. break;
  18. case 2: Delcontact(&s1);
  19. break;
  20. case 3: Modifycontact(&s1);
  21. break;
  22. case 4: Showcontact(&s1);
  23. break;
  24. case 5: Findcontact(&s1);
  25. break;
  26. case 0: Destorycontact(&s1);
  27. break;
  28. default: printf("输入错误,请重新输入!\n");
  29. break;
  30. }
  31. } while (num);
  32. return 0;
  33. }

小提示

写代码的时候第一次运行发现报错很多也别慌,看报错的内容一个个找原因就好。

主要是逻辑上没有错误,没有遗漏情况,没有数组越界,没有释放空指针等。然后是代码有没有问题,函数名是否写错,重复定义,漏写头文件,取地址,类型错误等等。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/1006915
推荐阅读
相关标签
  

闽ICP备14008679号