赞
踩
首先简要介绍顺序表和链表的概念和区别以作区分。
顺序表:逻辑上是线性的,物理性质上也是线性的。逻辑是线性的(连续的)体现在它可以通过第一个数找到接下来的数。物理性质上的线性体现在分配给它的内存是连续的。它本质上就像一个数组,可以通过下标来访问成员。
单链表:这里说的单链表是指不带头单向不循环链表。链表和顺序表是不同的。链表在逻辑上是线性的,但在物理性质上是非线性的。需要的时候申请一块内存,但这块内存和其他内存不一定连续,它可能是散落在四周的,只是通过记住下一个节点的位置,使这些单独存在的空间有了联系。它们在逻辑上就像一条串好的线。一个结点接一个结点。就像火车一样,需要的时候装个车厢,不需要就把车厢撤下来。
顺序表和链表的对比
顺序表:
缺点:
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
用注释说明了。
头文件里主要写你想要实现哪些功能,就声明哪些函数。先写的时候可以不完整,后面想到再加。
- #define _CRT_SECURE_NO_WARNINGS
- #pragma warning(disable:6031)
- #pragma once//防止被重复引用
- #define NAME_MAX 100
- #define SEX_MAX 10
- #define TEL_MAX 14
- #define ADDR_MAX 100
- #include <stdio.h>
- #include <string.h>
- #include <assert.h>
- #include <stdlib.h>
-
- typedef struct personinfo //联系人是一个结构体,这个结构体里面包含有五个信息。
- {
- char name[NAME_MAX];
- char sex[SEX_MAX];
- int age; //其他信息是数组,因为单个char内存太小不够写,但年龄只需要整型就够
- char tel[TEL_MAX];
- char addr[ADDR_MAX];
- }personinfo; //把struct personinfo重命名为personinfo
-
-
- typedef struct seqlist //整个通讯录是一个结构体,这个结构体里面有联系人数组
- {
- personinfo* info;//这是一个联系人数组,里面有各个联系人信息
- int capacity;//这个是通讯录的空间大小,可以装多少联系人
- int size; //这个是通讯录的实际大小,装了多少个联系人
- }contact;
-
- void Menu(); //菜单的声明 下面是各个功能实现的函数。
- //初始化通讯录
- void Initcontact(contact* con);//传一级指针是因为要改变con的内容,下面保持接口一致性
- //扩容
- void Creatcapacity(contact* con);
- //添加联系人
- void Addcontact(contact* con);
- //删除联系人
- void Delcontact(contact* con);
- //展示联系人
- void Showcontact(contact* con);
- //查找联系人
- void Findcontact(contact* con);
- //修改联系人
- void Modifycontact(contact* con);
- //销毁通讯录
- void Destorycontact(contact* con);
-
2.再写sqcontact.c文件
这里首先要包含头文件的内容。然后去实现头文件的那些函数。
写完一些功能的时候可以测试一下这些代码是否无误。
- #define _CRT_SECURE_NO_WARNINGS
- #pragma warning(disable:6031)
- #include "sqcontact.h"
-
- void Menu()
- {
- printf("\n");
- printf("***************************\n");
- printf("1.添加联系人\n");
- printf("2.删除联系人\n");
- printf("3.修改联系人\n");
- printf("4.展示联系人\n");
- printf("5.查找联系人\n");
- printf("0.退出通讯录\n");
- printf("***************************\n\n");
- }
-
- //初始化通讯录
- void Initcontact(contact* con)
- {
- con->info = NULL; //初始化通讯录就是把里面的指针置为NULL,其他赋值。
- con->capacity = 0; //也可以把指针赋值,只是为了避免它成为野指针。
- con->size = 0;
- }
-
- //扩容
- void Creatcapacity(contact* con)
- {
- if (con->capacity == con->size)//如果容量和实际数据相等(包括0),就需要扩容。
- {
- int newcapacity = con->capacity == 0 ? 4 : (2 * con->capacity);
- personinfo* ptr = (personinfo*)realloc(con->info, sizeof(personinfo) * newcapacity);
- if (ptr == NULL)
- {
- perror("realloc failured");
- exit(1);
- }
- con->info = ptr;
- con->capacity = newcapacity;
- }//还有容量则什么都不做
- }
- //尾插
- void Addback(contact* con, personinfo* x)
- {
- //一种是直接尾插,一种是头插
- if (con->size==0) //头插,因为联系人里没有数据
- {
- Creatcapacity(con);//尾插的时候要看一下空间够不够
- con->info[0] = *x;
- con->size++;
- }
- else//尾插,联系人中有数据
- {
- Creatcapacity(con);
- con->info[con->size] = *x;
- con->size++;
- }
- }
-
- //添加联系人 包含扩容,尾插
- void Addcontact(contact* con)
- {
- personinfo x; //创建一个联系人结构体,填充内容,再把它传给尾插的函数。
- Creatcapacity(con); //这句可以不写,后面尾插的时候扩容了的
- printf("请输入您要添加的联系人姓名:\n");
- scanf("%s", x.name); //数组名是首元素地址,所以不用再取地址了
- printf("请输入您要添加的联系人性别:\n");
- scanf("%s", x.sex);
- printf("请输入您要添加的联系人年龄:\n");
- scanf("%d", &x.age); //注意,这里要取地址。这个是整型
- printf("请输入您要添加的联系人电话:\n");
- scanf("%s", x.tel);
- printf("请输入您要添加的联系人地址:\n");
- scanf("%s", x.addr);
-
- Addback(con, &x);
- printf("联系人添加成功!\n");
- Showcontact(con);//添加成功以后再展示一遍当前通讯录
- }
-
- //指定位置删除联系人
- void Del(contact* con, int pos)
- {
- for (int i = pos; i < con->size-1; i++)//i如果是最后一个,下标为size-1的数,那么
- {
- con->info[i] = con->info[i + 1];
- }
- con->size--;//通过size--,最后一个数也被删掉了,这个逻辑就完整了。(访问不了等于删掉
- }
- //删除联系人包含 找联系人 删除联系人
- void Delcontact(contact* con)
- {
- assert(con);
- printf("请输入您要删除的联系人:\n");
- char name[NAME_MAX];
- scanf("%s", name);
-
- for (int i = 0; i < con->size; i++)
- {
- if (strcmp(con->info[i].name, name) == 0)//找联系人
- {
- //任意位置删除联系人
- Del(con, i);
- printf("联系人删除成功\n\n");
- Showcontact(con);
- return;
- }
- }
- printf("该联系人不存在!\n");
-
- }
-
- //展示联系人
- void Showcontact(contact* con)
- {
- if (con == NULL)
- {
- return;
- }
- printf("当前联系人如下:\n");
- printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址");
- for (int i = 0; i < con->size; i++)
- {
- printf("%-4s ", con->info[i].name);
- printf("%-4s ", con->info[i].sex);
- printf("%-4d ", con->info[i].age);
- printf("%-4s ", con->info[i].tel);
- printf("%-4s ", con->info[i].addr);
- printf("\n");
- }
- }
-
- //查找联系人 这个代码和前面有很多重复部分,找联系人。直接复制之前的代码再修改一下
- void Findcontact(contact* con)
- {
- assert(con);
- printf("请输入您要查找的联系人:\n");
- char name[NAME_MAX];
- scanf("%s", name);
-
- for (int i = 0; i < con->size; i++)
- {
- if (strcmp(con->info[i].name, name) == 0)
- {
- printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址");
- printf("%-4s ", con->info[i].name);
- printf("%-4s ", con->info[i].sex);
- printf("%-4d ", con->info[i].age);
- printf("%-4s ", con->info[i].tel);
- printf("%-4s ", con->info[i].addr);
- printf("\n");
- return;
- }
- }
- printf("该联系人不存在!\n");
- }
-
- //修改联系人 找联系人,再改联系人。这个代码和前面代码有很多重复部分。直接复制过来再改改
- void Modifycontact(contact* con)
- {
- assert(con);
- printf("请输入您要修改的联系人:\n");
- char name[NAME_MAX];
- scanf("%s", name);
-
- for (int i = 0; i < con->size; i++)
- {
- if (strcmp(con->info[i].name, name) == 0)
- {
- printf("请输入您要修改的联系人姓名:\n");
- scanf("%s", con->info[i].name);
- printf("请输入您要修改的联系人性别:\n");
- scanf("%s", con->info[i].sex);
- printf("请输入您要修改的联系人年龄:\n");
- scanf("%d", &con->info[i].age);
- printf("请输入您要修改的联系人电话:\n");
- scanf("%s", con->info[i].tel);
- printf("请输入您要修改的联系人地址:\n");
- scanf("%s", con->info[i].addr);
- printf("联系人修改成功!\n");
- Showcontact(con);
- return;
- }
- }
- printf("该联系人不存在!\n");
- }
-
- //销毁通讯录
- void Destorycontact(contact* con)
- {
- assert(con); //不能释放已经释放的动态内存
- free(con->info); //释放内存空间
- con->info = NULL;//再把指针置为空
- con->size = con->capacity = 0;//其他变为0
- }
3.写test.c文件
可以先实现一部分功能以后就写test.c文件测试一下那些功能是否能顺利运行。
首先还是包含一个头文件。初始化和创建通讯录不能在do while循环里。测试文件相比比较简单。
- #define _CRT_SECURE_NO_WARNINGS
- #pragma warning(disable:6031)
- #include "sqcontact.h"
-
-
- int main()
- {
- contact s1;
- Initcontact(&s1);
- int num = 0;
- do
- {
- Menu();
- printf("请输入您要进行的操作:\n");
- scanf("%d", &num);
- switch (num)
- {
- case 1: Addcontact(&s1);
- break;
- case 2: Delcontact(&s1);
- break;
- case 3: Modifycontact(&s1);
- break;
- case 4: Showcontact(&s1);
- break;
- case 5: Findcontact(&s1);
- break;
- case 0: Destorycontact(&s1);
- break;
- default: printf("输入错误,请重新输入!\n");
- break;
- }
-
- } while (num);
-
- return 0;
- }
写代码的时候第一次运行发现报错很多也别慌,看报错的内容一个个找原因就好。
主要是逻辑上没有错误,没有遗漏情况,没有数组越界,没有释放空指针等。然后是代码有没有问题,函数名是否写错,重复定义,漏写头文件,取地址,类型错误等等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。