目录
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)⾄少能够存储100个⼈的通讯信息
2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显⽰联系⼈信息
首先,我们要使用上一期学习的动态顺序表实现通讯录,如果有不知道的小伙伴,可以移步到小江的上一篇博客:学习笔记---超基础+详细+新手的顺序表~~-CSDN博客动态顺序表的简单实现https://blog.csdn.net/2301_79184587/article/details/133842555 我们知道顺序表可以存储数据,但是通讯录存储比较多,所以我们把一个联系人的所有信息作为一个整体存储到顺序表,再进行对应接口的编写:
实现顺序表的创建,初始化,一系列具体操作,销毁
一系列具体操作:
头部/尾部增加联系⼈信息、头部/尾部删除指定联系⼈、查找制定联系⼈、指定位置修改联系⼈信息、显⽰联系⼈信息
注意⚠️
我们是在顺序表的基础上实现通讯录,所以我们直接在上一篇的动态顺序表的基础上实现
//创建保存联系人数据的结构体
//定义联系人数据字节大小
#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);
对于结构体中的数组创建是要定长数组还是动态数组?
我们使用定长数组,虽然无法确定内容大小,但是根据常识,可以给出数组的最大容量
注意⚠️
CInfo只是在contact.h中的结构体重命名,但是别的文件不知道有这回事,所以不认识cinfo--->我们需要包含头文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
#include"contact.h"//包含头文件
#include
//创建动态顺序表
//typedef int SLDataType;//方便以后修改数据类型,因为我们不一定每次都需要int类型
//替换
typedef struct CInfo SLDataType;//注意CInfo
#include"Seqlist.h"
#include"contact.h"
//通讯录的初始化和销毁
void ContactInit(contact* con)
{
SLInit(con);//直接调用顺序表的初始化
}
void ContactDestroy(contact* con)
{
SLDestroy(con);//直接调用顺序表的销毁
}
#include"Seqlist.h"
#include"contact.h"
//contact
void contact01()
{
//创建+初始化通讯录
contact con;
ContactInit(&con);
//销毁通讯录
ContactDestroy(&con);
}
int main()
{
contact01();
return 0;
}
我们发现疯狂报错,这是为什么呢?
例如我们先解决这个问题:原来的是int类型的,而新的SLDataType是struct类型的,所以==非法--->我们要修改
ps->a[I].name==x
......(一个一个比较)
这里为了方便,我们直接注释掉查找数据的相关代码
对于这个错误,我们还是把重命名修改一下,不要用CnInfo了(contact.h)
#pragma once
#include
#include
#include
#include
#include"contact.h"//包含头文件
//创建动态顺序表
//typedef int SLDataType;//方便以后修改数据类型,因为我们不一定每次都需要int类型
//替换
typedef struct ContactInfo SLDataType;
//添加+删除联系人
void ContactAdd(contact* pcon);
void ContactcDel(contact* pcon);
//添加联系人
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);//调用顺序表的删除指定位置的数据
}
}
#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;
}
//修改联系人
void ContactChange(contact* pcon);
//修改联系人
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");
}
}
#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;
}
但是,我们发现有时候修改联系人的时候,我们可能只需要修改年龄or地址单个数据,或者几个数据,不是全都要修改,所以要优化一下啊
我们可以添加菜单,让使用者选择要修改的项目,在原来的代码基础上修改,从而达到只修改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);
}
}
#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个联系人
//查看通讯录
void ContactShow(contact* pcon);
//查看通讯录
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
);
}
}
#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;
}
//查找指定联系人
void ContactFind(contact* pcon);
//查找指定联系人
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
);
}
}
#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;
}
为了更加美观,我们制作一个菜单界面
//为了界面更加美观--->创建菜单界面
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:遍历一遍数组,若值为val,执行删除操作
解法2:使用双指针
定义2个指针src=0和dst=0遍历数组
1)src指向的数据是val
src++;
2)src指向的数据不是val
src指向的数据赋给dst的位置
dst++;
src++;
int removeElement(int* nums, int numsSize, int val){
int size=0;
for(int i=0;i
int removeElement(int* nums, int numsSize, int val){
int src=0,dst=0;
while(src
2.2.1 题目
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--];
}
}
1. 中间/头部的插⼊删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷⻉数据,释放旧空间。会有不⼩的消耗。
3. 增容⼀般是呈2倍的增⻓,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插⼊了5个数据,后⾯没有数据插⼊了,那么就浪费了95个数据空间。
思考:如何解决以上问题呢?