目录
1. 基于动态顺序表实现通讯录项⽬
1.1 通讯录📇功能要求
1.2 总体思路分析🧐
1.3 创建+初始化+销毁顺序表🌞
1.3.1 contact.h
1.3.2 Seqlist.h
1.3.3 contact.c
1.3.4 text.c
1.3.5 代码运行测试
1.3.6 二次代码测试
1.4 添加+删除联系人👻
1.4.1 contatc.h
1.4.2 contact.c
1.4.3 tets.c
1.4.4 代码运行测试
1.5 修改联系人🙉
1.5.1 contatc.h
1.5.2 contact.c
1.5.3 tets.c
1.5.4 代码运行测试
1.5.5 contact.h优化🍎
1.5.6 test.c修改
1.5.7 二次代码运行测试
1.6 查看通讯录🥑
1.6.1 contatc.h
1.6.2 contact.c
1.6.3 tets.c
1.6.4 代码运行测试
1.7 查找指定联系人👤
1.7.1 contatc.h
1.7.2 contact.c
1.7.3 tets.c
1.7.4 代码运行测试
1.8 美观---菜单界面🏵️
1.8.1 test.c
1.9 通讯录📇代码运行测试
2. 顺序表经典算法
2.1 经典算法OJ题1: 移除元素🚨
2.1.1 题目
2.1.2 思路分析
2.1.3 代码实现
2.1.3.1 解法1
2.1.3.2 解法2
2.2 经典算法OJ题2: 合并两个有序数组☎️
2.2.2 思路分析
2.2.3 代码实现
3. 顺序表的问题及思考
1. 基于动态顺序表实现通讯录项⽬
1.1 通讯录📇功能要求
首先,我们看看通讯录需要实现什么功能:
1)⾄少能够存储100个⼈的通讯信息
2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显⽰联系⼈信息
1.2 总体思路分析🧐
首先,我们要使用上一期学习的动态顺序表实现通讯录📇,如果有不知道的小伙伴,可以移步到小江的上一篇博客:学习笔记---超基础+详细+新手的顺序表~~-CSDN博客动态顺序表的简单实现https://blog.csdn.net/2301_79184587/article/details/133842555 我们知道顺序表可以存储数据,但是通讯录存储比较多,所以我们把一个联系人的所有信息作为一个整体存储到顺序表,再进行对应接口的编写:
实现顺序表的创建,初始化,一系列具体操作,销毁
一系列具体操作:
头部/尾部增加联系⼈信息、头部/尾部删除指定联系⼈、查找制定联系⼈、指定位置修改联系⼈信息、显⽰联系⼈信息
注意⚠️
我们是在顺序表的基础上实现通讯录📇,所以我们直接在上一篇的动态顺序表的基础上实现📇
1.3 创建+初始化+销毁顺序表🌞
1.3.1 contact.h
//创建保存联系人数据的结构体
//定义联系人数据字节大小
#define NAME_MAX 100
#define SEX_MAX 10
#define PHONE_MAX 15
#define ADDR_MAX 100
typedef struct ContactInfo
{
//采用定长数组
char name[NAME_MAX];
char sex[SEX_MAX] ;
int age;
char phone[PHONE_MAX] ;
char addr[ADDR_MAX] ;
}CInfo;//重命名之后更加简洁方便
//通讯录的初始化和销毁
void ContactInit(contact* con);
void ContactDestroy(contact* con);
对于结构体中的数组创建是要定长数组还是动态数组?
我们使用定长数组,虽然无法确定内容大小,但是根据常识,可以给出数组的最大容量
1.3.2 Seqlist.h
注意⚠️
CInfo只是在contact.h中的结构体重命名,但是别的文件不知道有这回事,所以不认识cinfo--->我们需要包含头文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include"contact.h"//包含头文件
#include<string.h>
//创建动态顺序表
//typedef int SLDataType;//方便以后修改数据类型,因为我们不一定每次都需要int类型
//替换
typedef struct CInfo SLDataType;//注意CInfo
1.3.3 contact.c
#include"Seqlist.h"
#include"contact.h"
//通讯录的初始化和销毁
void ContactInit(contact* con)
{
SLInit(con);//直接调用顺序表的初始化
}
void ContactDestroy(contact* con)
{
SLDestroy(con);//直接调用顺序表的销毁
}
1.3.4 text.c
#include"Seqlist.h"
#include"contact.h"
//contact
void contact01()
{
//创建+初始化通讯录
contact con;
ContactInit(&con);
//销毁通讯录
ContactDestroy(&con);
}
int main()
{
contact01();
return 0;
}
1.3.5 代码运行测试
我们发现疯狂报错,这是为什么呢?
例如我们先解决这个问题:原来的是int类型的,而新的SLDataType是struct类型的,所以==非法--->我们要修改
ps->a[I].name==x
......(一个一个比较)
这里为了方便,我们直接注释掉查找数据的相关代码
对于这个错误,我们还是把重命名修改一下,不要用CnInfo了(contact.h)
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include"contact.h"//包含头文件
//创建动态顺序表
//typedef int SLDataType;//方便以后修改数据类型,因为我们不一定每次都需要int类型
//替换
typedef struct ContactInfo SLDataType;
1.3.6 二次代码测试
1.4 添加+删除联系人👻
1.4.1 contatc.h
//添加+删除联系人
void ContactAdd(contact* pcon);
void ContactcDel(contact* pcon);
1.4.2 contact.c
//添加联系人
void ContactAdd(contact* pcon)
{
//接下来要获取的数据都是CInfo结构体里我们设置的数据
CInfo info;
printf("请输入要添加的联系人的姓名:");
scanf("%s", info.name);//name是数组名--->本来就是地址
printf("请输入请输入要添加的联系人的性别:");
scanf("%s", info.sex);
printf("请输入要添加的联系人的年龄:");
scanf("%d", &info.age);//age是int类型的数据--->取地址
printf("请输入要添加的联系人的号码:");
scanf("%s", info.phone);
printf("请输入要添加的联系人的地址:");
scanf("%s", info.addr);
//数据获取到之后存储到info中
//接下来,我们需要在顺序表中插入数据
SLpushBack(pcon, info);//直接调用顺序表的尾插
}
//删除联系人
//由于删除/添加/查找联系人都需要判断联系人是否存在,所以我们把判断联系人是否存在单独写出来
int FindByName(contact* pcon, char name[])
{
for (int i = 0; i < pcon->size; i++)
{
if (strcmp(pcon->a[i].name, name) == 0)//strcmp比较字符串大小
return i;
}
return -1;
}
void ContactcDel(contact* pcon)
{
//我们需要使用联系人众多信息中的一项来查找联系人是否存在
//这里直接要求输入联系人的姓名进行查找
printf("请输入要删除的联系人的姓名:");
//创建一个name数组存储要输入的姓名
char name[NAME_MAX];
scanf("%s", name);
//调用函数判断联系人是否存在
//1.存在--->根据返回的下标删除--->即为调用顺序表的删除指定位置的数据
//2.不存在--->说明要删除的联系人不存在
int ret=FindByName(pcon, name);
if (ret < 0)
{
printf("要求删除的联系人不存在!\n");
return 1;
}
else
{
SLErase(pcon, ret);//调用顺序表的删除指定位置的数据
}
}
1.4.3 tets.c
#include"Seqlist.h"
#include"contact.h"
//contact
void contact01()
{
//创建+初始化通讯录
contact con;
ContactInit(&con);
//添加联系人
ContactAdd(&con);
ContactAdd(&con);
ContactAdd(&con);
//删除联系人
ContactcDel(&con);
//销毁通讯录
ContactDestroy(&con);
}
int main()
{
contact01();
return 0;
}
1.4.4 代码运行测试
1.5 修改联系人🙉
1.5.1 contatc.h
//修改联系人
void ContactChange(contact* pcon);
1.5.2 contact.c
//修改联系人
void ContactChange(contact* pcon)
{
//我们需要使用联系人众多信息中的一项来查找联系人是否存在
//这里直接要求输入联系人的姓名进行查找
//创建一个name数组存储要输入的姓名
char name[NAME_MAX];
printf("请输入要修改的联系人的姓名:");
scanf("%s", name);
//调用函数判断联系人是否存在
//1.存在--->根据返回的下标修改
//2.不存在--->说明要修改的联系人不存在
int ret = FindByName(pcon, name);
if (ret < 0)
{
printf("要求修改的联系人不存在!\n");
return 1;
}
else
{
//底层逻辑是顺序表--->在顺序表中修改对应的下标的结构体中的各项数据
printf("请输入新的联系人的姓名:");
scanf("%s", pcon->a[ret].name);//name是数组名--->本来就是地址
printf("请输入请输入新的联系人的性别:");
scanf("%s", pcon->a[ret].sex);
printf("请输入新的联系人的年龄:");
scanf("%d", &pcon->a[ret].age);//age是int类型的数据--->取地址
printf("请输入新的联系人的号码:");
scanf("%s", pcon->a[ret].phone);
printf("请输入新的联系人的地址:");
scanf("%s", pcon->a[ret].addr);
printf("修改成功!\n");
}
}
1.5.3 tets.c
#include"Seqlist.h"
#include"contact.h"
//contact
void contact01()
{
//创建+初始化通讯录
contact con;
ContactInit(&con);
//添加联系人
ContactAdd(&con);
ContactAdd(&con);
ContactAdd(&con);
//删除联系人
ContactDel(&con);
//修改联系人
ContactChange(&con);
//销毁通讯录
ContactDestroy(&con);
}
int main()
{
contact01();
return 0;
}
1.5.4 代码运行测试
但是,我们发现有时候修改联系人的时候,我们可能只需要修改年龄or地址单个数据,或者几个数据,不是全都要修改,所以要优化一下啊
1.5.5 contact.h优化🍎
我们可以添加菜单,让使用者选择要修改的项目,在原来的代码基础上修改,从而达到只修改1项或者几项的目的
//修改联系人
void ContactChange(contact* pcon)
{
//我们需要使用联系人众多信息中的一项来查找联系人是否存在
//这里直接要求输入联系人的姓名进行查找
//创建一个name数组存储要输入的姓名
char name[NAME_MAX];
printf("请输入要修改的联系人的姓名:");
scanf("%s", name);
//调用函数判断联系人是否存在
//1.存在--->根据返回的下标修改
//2.不存在--->说明要修改的联系人不存在
int ret = FindByName(pcon, name);
if (ret < 0)
{
printf("要求修改的联系人不存在!\n");
return 1;
}
else
{
int a = -1;
do {
printf("*******************************************\n");
printf("*******************通讯录******************\n");
printf("***********1.修改姓名 2.修改性别***********\n");
printf("***********3.修改年龄 4.修改号码***********\n");
printf("***********5.修改地址 6.退出修改***********\n");
printf("请选择你的操作:\n");
scanf("%d", &a);
//底层逻辑是顺序表--->在顺序表中修改对应的下标的结构体中的各项数据
switch (a)
{
case 1:
printf("请输入新的联系人的姓名:");
scanf("%s", pcon->a[ret].name);//name是数组名--->本来就是地址
break;
case 2:
printf("请输入请输入新的联系人的性别:");
scanf("%s", pcon->a[ret].sex);
break;
case 3:
printf("请输入新的联系人的年龄:");
scanf("%d", &pcon->a[ret].age);//age是int类型的数据--->取地址
break;
case 4:
printf("请输入新的联系人的号码:");
scanf("%s", pcon->a[ret].phone);
break;
case 5:
printf("请输入新的联系人的地址:");
scanf("%s", pcon->a[ret].addr);
break;
case 6:
printf("退出修改联系人的界面!\n");
break;
default:
printf("输入有误!请重新输入:\n");
break;
}
} while (a != 6);
}
}
1.5.6 test.c修改
#include"Seqlist.h"
#include"contact.h"
//contact
void contact01()
{
//创建+初始化通讯录
contact con;
ContactInit(&con);
//添加联系人
ContactAdd(&con);
//ContactAdd(&con);
ContactAdd(&con);
//删除联系人
//ContactDel(&con);
//修改联系人
ContactChange(&con);
//销毁通讯录
ContactDestroy(&con);
}
int main()
{
contact01();
return 0;
}
为了方便,我们就先注释掉删除联系人的操作,并且只添加2个联系人
1.5.7 二次代码运行测试
1.6 查看通讯录🥑
1.6.1 contatc.h
//查看通讯录
void ContactShow(contact* pcon);
1.6.2 contact.c
//查看通讯录
void ContactShow(contact* pcon)
{
//打印通讯录存储的所有数据
//为了更加好看--->我们先输出表头
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "号码", "地址");
for (int i = 0; i < pcon->size; i++)
{
printf("%-4s %-4s %-4d %-4s %-10s\n",//表头对齐--->美观
pcon->a[i].name,
pcon->a[i].sex,
pcon->a[i].age,
pcon->a[i].phone,
pcon->a[i].addr
);
}
}
1.6.3 tets.c
#include"Seqlist.h"
#include"contact.h"
//contact
void contact01()
{
//创建+初始化通讯录
contact con;
ContactInit(&con);
//添加联系人
ContactAdd(&con);
ContactAdd(&con);
ContactAdd(&con);
//删除联系人
ContactDel(&con);
//修改联系人
ContactChange(&con);
//查看通讯录
ContactShow(&con);
//销毁通讯录
ContactDestroy(&con);
}
int main()
{
contact01();
return 0;
}
1.6.4 代码运行测试
1.7 查找指定联系人👤
1.7.1 contatc.h
//查找指定联系人
void ContactFind(contact* pcon);
1.7.2 contact.c
//查找指定联系人
void ContactFind(contact* pcon)
{
//我们需要使用联系人众多信息中的一项来查找联系人是否存在
//这里直接要求输入联系人的姓名进行查找
//创建一个name数组存储要输入的姓名
char name[NAME_MAX];
printf("请输入要查找的联系人的姓名:");
scanf("%s", name);
//调用函数判断联系人是否存在
//1.存在--->根据返回的下标查找并打印出来
//2.不存在--->说明要查找的联系人不存在
int ret = FindByName(pcon, name);
if (ret < 0)
{
printf("要求查找的联系人不存在!\n");
return 1;
}
else
{
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "号码", "地址");
printf("%-4s %-4s %-4d %-4s %-4s\n",//表头对齐--->美观
pcon->a[ret].name,
pcon->a[ret].sex,
pcon->a[ret].age,
pcon->a[ret].phone,
pcon->a[ret].addr
);
}
}
1.7.3 tets.c
#include"Seqlist.h"
#include"contact.h"
//contact
void contact01()
{
//创建+初始化通讯录
contact con;
ContactInit(&con);
//添加联系人
ContactAdd(&con);
ContactAdd(&con);
ContactAdd(&con);
//删除联系人
ContactDel(&con);
//修改联系人
ContactChange(&con);
//查看通讯录
ContactShow(&con);
//查找指定联系人
ContactFind(&con);
//销毁通讯录
ContactDestroy(&con);
}
int main()
{
contact01();
return 0;
}
1.7.4 代码运行测试
1.8 美观---菜单界面🏵️
为了更加美观,我们制作一个菜单界面
1.8.1 test.c
//为了界面更加美观--->创建菜单界面
void menu1()
{
printf("***********************************************\n");
printf("*********************通讯录********************\n");
printf("***********1.添加联系人 2.删除联系人***********\n");
printf("***********3.修改联系人 4.查找联系人***********\n");
printf("***********5.查看通讯录 6.退出通讯录***********\n");
}
int main()
{
int a = -1;
//初始化+创建通讯录
contact con;
ContactInit(&con);
//一系列操作
do {
menu1();
printf("请选择你的操作:\n");
scanf("%d", &a);
switch (a)
{
case 1:
ContactAdd(&con);
break;
case 2:
ContactDel(&con);
break;
case 3:
ContactChange(&con);
break;
case 4:
ContactShow(&con);
break;
case 5:
ContactFind(&con);
break;
case 6:
printf("退出通讯录界面!\n");
break;
default:
printf("选择错误!请重新选择:\n");
break;
}
} while (a != 6);
//销毁通讯录
ContactDestroy(&con);
return 0;
}
1.9 通讯录📇代码运行测试
2. 顺序表经典算法
2.1 经典算法OJ题1: 移除元素🚨
2.1.1 题目
2.1.2 思路分析
解法1:遍历一遍数组,若值为val,执行删除操作
解法2:使用双指针
定义2个指针src=0和dst=0遍历数组
1)src指向的数据是val
src++;
2)src指向的数据不是val
src指向的数据赋给dst的位置
dst++;
src++;
2.1.3 代码实现
2.1.3.1 解法1
int removeElement(int* nums, int numsSize, int val){
int size=0;
for(int i=0;i<numsSize;i++)
{
if(nums[i]!=val)
{
nums[size++]=nums[i];
}
}
return size;
}
2.1.3.2 解法2
int removeElement(int* nums, int numsSize, int val){
int src=0,dst=0;
while(src<numsSize)
{
if(nums[src]==val)
src++;
else
{
nums[dst]=nums[src];
dst++;
src++;
}
}
return dst;
}
2.2 经典算法OJ题2: 合并两个有序数组☎️
2.2.1 题目
2.2.2 思路分析
2.2.3 代码实现
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int l1=m-1,l2=n-1;
int l3=m+n-1;
while(l1>=0&&l2>=0)
{
if(nums1[l1]>nums2[l2])
nums1[l3--]=nums1[l1--];
else
{
nums1[l3--]=nums2[l2--];
}
}
while(l2>=0)
{
nums1[l3--]=nums2[l2--];
}
}
3. 顺序表的问题及思考
1. 中间/头部的插⼊删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷⻉数据,释放旧空间。会有不⼩的消耗。
3. 增容⼀般是呈2倍的增⻓,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插⼊了5个数据,后⾯没有数据插⼊了,那么就浪费了95个数据空间。
思考:如何解决以上问题呢?