顺序表的应用

文章目录

目录

  • 基于动态顺序表实现通讯录项目
  • 顺序表经典算法
  • 顺序表的问题及思考

1. 基于动态顺序表实现通讯录项目

我们先写一个框架:

c 复制代码
//Contact.h

#include <stdio.h>//暂时加上
c 复制代码
//ConTest.c

#include "Contact.h"

//通讯录菜单
void menu()
{
	printf("******************通讯录******************\n");
	printf("*******1. 添加联系人  2.删除联系人********\n");
	printf("*******3. 修改联系人  4.查找联系人********\n");
	printf("*******5. 查看通讯录  0.  退 出   ********\n");
	printf("******************************************\n");
}

int main()
{
	int op = -1;

	do
	{
		menu();
		printf("请选择您的操作:\n");
		scanf("%d", &op);

		switch (op)
		{
		case 1:
			//添加联系人
			break;
		case 2:
			//删除联系人
			break;
		case 3:
			//修改联系人
			break;
		case 4:
			//查找联系人
			break;
		case 5:
			//查看通讯录
			break;
		case 0:
			//退出通讯录
			printf("通讯录退出中...\n");
			break;
		default:
			break;
		}
	} while (op != 0);

	return 0;
}

接着,我们就可以开始添加具体操作了:

先要创建通讯录数据类型:

c 复制代码
//Contact.h

#define NAME_MAX 100
#define GENDER_MAX 10
#define TEL_MAX 12
#define ADDR_MAX 100

//通讯录数据类型
typedef struct PersonInfo
{
	char name[NAME_MAX];
	int age;
	char gender[GENDER_MAX];
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Info;

我们要把之前写的顺序表中数组的类型进行替换:

c 复制代码
//SeqList.h

#include "Contact.h"

typedef Info SLDataType;

然后我们写接口:

c 复制代码
//Contact.h

//通讯录提供的操作

typedef struct SeqList Contact;

//通讯录的初始化和销毁
void ContactInit(Contact* pcon);//实际初始化的还是顺序表

这里我们想把 SL 换成 Contact,这样看上去更好理解,所以就要 typedef struct SeqList Contact; ,但是要使用struct SeqList 就要 #include "SeqList.h" ,但是这样会出现一个问题:

解决办法:前置声明

c 复制代码
//Contact.h

//使用顺序表的前置声明
struct SeqList;

typedef struct SeqList Contact;

//通讯录提供的操作

//通讯录的初始化和销毁
void ContactInit(Contact* pcon);//实际初始化的还是顺序表
void ContactDestroy(Contact* pcon);

//增加、删除、修改、查找、查看通讯录
void ContactAdd(Contact* pcon);
void ContactDel(Contact* pcon);
void ContactModify(Contact* pcon);
void ContactFind(Contact* pcon);
void ContactShow(Contact* pcon);

此外,以下代码不再适用,直接注释掉:

c 复制代码
//SeqList.c

//在顺序表中查找x
//int SLFind(SL* ps, SLDataType x)
//{
//	//加上断言对代码的健壮性更好
//	assert(ps);
//
//	for (int i = 0; i < ps->size; i++)
//	{
//		if (x == ps->arr[i])
//		{
//			return i;
//		}
//	}
//
//	return -1;
//}

因为现在 ps->arr[i] 已经变成一个结构体了,不能直接使用 == 来比较

接下来就是方法的具体实现:

c 复制代码
//Contact.c

//#include "Contact.h"
#include "SeqList.h"

//通讯录的初始化和销毁
//SL* ps
void ContactInit(Contact* pcon)
{
	SLInit(pcon);
}

void ContactDestroy(Contact* pcon)
{
	SLDestroy(pcon);
}

//增加、删除、修改、查找、查看通讯录
void ContactAdd(Contact* pcon)
{
	//创建联系人结构体变量
	Info info;

	printf("请输入联系人姓名:\n");
	scanf("%s", info.name);
	printf("请输入联系人年龄:\n");
	scanf("%d", &info.age);
	printf("请输入联系人性别:\n");
	scanf("%s", info.gender);
	printf("请输入联系人电话:\n");
	scanf("%s", info.tel);
	printf("请输入联系人住址:\n");
	scanf("%s", info.addr);

	//保存数据到通讯录(顺序表)
	SLPushBack(pcon, info);
}

void ContactShow(Contact* pcon)
{
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");

	for (int i = 0; i < pcon->size; i++)
	{
		printf("%s %s %d %s %s\n",
				pcon->arr[i].name,
				pcon->arr[i].gender,
				pcon->arr[i].age,
				pcon->arr[i].tel,
				pcon->arr[i].addr
			);
	}
}

int FindByName(Contact* pcon, char name[])
{
	for (int i = 0; i < pcon->size; i++)
	{
		if (0 == strcmp(pcon->arr[i].name, name))
		{
			//找到了
			return i;
		}
	}

	return -1;
}

void ContactDel(Contact* pcon)
{
	//删除之前一定要先查找
	//找到了,可以删除
	//找不到,不能执行删除
	printf("请输入要删除的联系人姓名:\n");
	char name[NAME_MAX];
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);

	if (findIndex < 0)
	{
		printf("要删除的联系人不存在!\n");
		return;
	}

	//执行删除操作
	SLErase(pcon, findIndex);
	printf("联系人删除成功!\n");
}

void ContactModify(Contact* pcon)
{
	//修改之前要先查找
	//找到了,执行修改操作
	//没有找到,不能执行修改操作
	char name[NAME_MAX];
	printf("请输入要修改的联系人姓名\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);

	if (findIndex < 0)
	{
		printf("要修改的联系人不存在!\n");
		return;
	}

	//找到了,执行修改操作
	printf("请输入姓名:\n");
	scanf("%s", pcon->arr[findIndex].name);
	printf("请输入年龄:\n");
	scanf("%d", &pcon->arr[findIndex].age);
	printf("请输入性别:\n");
	scanf("%s", pcon->arr[findIndex].gender);
	printf("请输入电话:\n");
	scanf("%s", pcon->arr[findIndex].tel);
	printf("请输入地址:\n");
	scanf("%s", pcon->arr[findIndex].addr);

	printf("联系人修改成功!\n");
}

void ContactFind(Contact* pcon)
{
	char name[NAME_MAX];
	printf("请输入要查找的用户姓名:\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);

	if (findIndex < 0)
	{
		printf("该联系人不存在!\n");
		return;
	}

	//找到了,打印一下查找的联系人信息
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	printf("%s %s %d %s %s\n",
		pcon->arr[findIndex].name,
		pcon->arr[findIndex].gender,
		pcon->arr[findIndex].age,
		pcon->arr[findIndex].tel,
		pcon->arr[findIndex].addr
	);
}

完整代码:

c 复制代码
//SeqList.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "Contact.h"

//动态顺序表

typedef Info SLDataType;

typedef struct SeqList
{
	SLDataType* arr;//存储数据的底层结构
	int capacity;//记录顺序表的空间大小
	int size;//记录顺序表当前有效的数据个数
}SL;

//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);

//顺序表的尾部插入
void SLPushBack(SL* ps, SLDataType x);

//删除指定位置数据
void SLErase(SL* ps, int pos);
c 复制代码
//SeqList.c

#include "SeqList.h"

//初始化和销毁
void SLInit(SL* ps)
{
	assert(ps);

	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

void SLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newCapacity = 0 == ps->capacity ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));

		if (NULL == tmp)
		{
			perror("realloc fail!");
			exit(1);
		}

		//扩容成功
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}

//顺序表的尾部插入
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);

	//空间不够,扩容
	SLCheckCapacity(ps);

	//空间足够,直接插入
	ps->arr[ps->size++] = x;
}

//删除指定位置数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	//pos以后的数据往前挪动一位
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}

	ps->size--;
}

void SLDestroy(SL* ps)
{
	assert(ps);

	free(ps->arr);
	ps->arr = NULL;

	ps->size = ps->capacity = 0;
}
c 复制代码
//Contact.h

#define NAME_MAX 100
#define GENDER_MAX 10
#define TEL_MAX 12
#define ADDR_MAX 100

//通讯录数据类型
typedef struct PersonInfo
{
	char name[NAME_MAX];
	int age;
	char gender[GENDER_MAX];
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Info;

//使用顺序表的前置声明
struct SeqList;

typedef struct SeqList Contact;

//通讯录提供的操作

//通讯录的初始化和销毁
void ContactInit(Contact* pcon);//实际初始化的还是顺序表
void ContactDestroy(Contact* pcon);

//增加、删除、修改、查找、查看通讯录
void ContactAdd(Contact* pcon);
void ContactDel(Contact* pcon);
void ContactModify(Contact* pcon);
void ContactFind(Contact* pcon);
void ContactShow(Contact* pcon);
c 复制代码
//Contact.c

#include "SeqList.h"

//通讯录的初始化和销毁
//SL* ps
void ContactInit(Contact* pcon)
{
	SLInit(pcon);
}

void ContactDestroy(Contact* pcon)
{
	SLDestroy(pcon);
}

//增加、删除、修改、查找、查看通讯录
void ContactAdd(Contact* pcon)
{
	//创建联系人结构体变量
	Info info;

	printf("请输入联系人姓名:\n");
	scanf("%s", info.name);
	printf("请输入联系人年龄:\n");
	scanf("%d", &info.age);
	printf("请输入联系人性别:\n");
	scanf("%s", info.gender);
	printf("请输入联系人电话:\n");
	scanf("%s", info.tel);
	printf("请输入联系人住址:\n");
	scanf("%s", info.addr);

	//保存数据到通讯录(顺序表)
	SLPushBack(pcon, info);
}

int FindByName(Contact* pcon, char name[])
{
	for (int i = 0; i < pcon->size; i++)
	{
		if (0 == strcmp(pcon->arr[i].name, name))
		{
			//找到了
			return i;
		}
	}

	return -1;
}

void ContactDel(Contact* pcon)
{
	//删除之前一定要先查找
	//找到了,可以删除
	//找不到,不能执行删除
	printf("请输入要删除的联系人姓名:\n");
	char name[NAME_MAX];
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);

	if (findIndex < 0)
	{
		printf("要删除的联系人不存在!\n");
		return;
	}

	//执行删除操作
	SLErase(pcon, findIndex);
	printf("联系人删除成功!\n");
}

void ContactModify(Contact* pcon)
{
	//修改之前要先查找
	//找到了,执行修改操作
	//没有找到,不能执行修改操作
	char name[NAME_MAX];
	printf("请输入要修改的联系人姓名\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);

	if (findIndex < 0)
	{
		printf("要修改的联系人不存在!\n");
		return;
	}

	//找到了,执行修改操作
	printf("请输入姓名:\n");
	scanf("%s", pcon->arr[findIndex].name);
	printf("请输入年龄:\n");
	scanf("%d", &pcon->arr[findIndex].age);
	printf("请输入性别:\n");
	scanf("%s", pcon->arr[findIndex].gender);
	printf("请输入电话:\n");
	scanf("%s", pcon->arr[findIndex].tel);
	printf("请输入地址:\n");
	scanf("%s", pcon->arr[findIndex].addr);

	printf("联系人修改成功!\n");
}

void ContactFind(Contact* pcon)
{
	char name[NAME_MAX];
	printf("请输入要查找的用户姓名:\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);

	if (findIndex < 0)
	{
		printf("该联系人不存在!\n");
		return;
	}

	//找到了,打印一下查找的联系人信息
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	printf("%s %s %d %s %s\n",
		pcon->arr[findIndex].name,
		pcon->arr[findIndex].gender,
		pcon->arr[findIndex].age,
		pcon->arr[findIndex].tel,
		pcon->arr[findIndex].addr
	);
}

void ContactShow(Contact* pcon)
{
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");

	for (int i = 0; i < pcon->size; i++)
	{
		printf("%s %s %d %s %s\n",
				pcon->arr[i].name,
				pcon->arr[i].gender,
				pcon->arr[i].age,
				pcon->arr[i].tel,
				pcon->arr[i].addr
			);
	}
}
c 复制代码
//ConTest.c

#include "SeqList.h"

//通讯录菜单
void menu()
{
	printf("******************通讯录******************\n");
	printf("*******1. 添加联系人  2.删除联系人********\n");
	printf("*******3. 修改联系人  4.查找联系人********\n");
	printf("*******5. 查看通讯录  0.  退 出   ********\n");
	printf("******************************************\n");
}

int main()
{
	int op = -1;
	//创建通讯录结构对象
	Contact con;
	ContactInit(&con);

	do
	{
		menu();
		printf("请选择您的操作:\n");
		scanf("%d", &op);

		switch (op)
		{
		case 1:
			//添加联系人
			ContactAdd(&con);
			break;
		case 2:
			//删除联系人
			ContactDel(&con);
			break;
		case 3:
			//修改联系人
			ContactModify(&con);
			break;
		case 4:
			//查找联系人
			ContactFind(&con);
			break;
		case 5:
			//查看通讯录
			ContactShow(&con);
			break;
		case 0:
			//退出通讯录
			printf("通讯录退出中...\n");
			break;
		default:
			break;
		}
	} while (op != 0);

	//销毁通讯录
	ContactDestroy(&con);

	return 0;
}

2.顺序表经典算法

2.1 移除元素

c 复制代码
int removeElement(int* nums, int numsSize, int val)
{
	//定义两个变量
	int src = 0, dst = 0;

	while (src < numsSize)
	{
		//nums[src] == val,src++
		//否则赋值,src和dst都++
		if (nums[src] == val)
		{
			src++;
		}
		else
		{
			//说明src指向位置的值不等于val
			nums[dst] = nums[src];
			dst++;
			src++;
		}
	}

	//此时dst的值刚好是数组的新长度
	return dst;
}

2.2 合并两个有序数组

c 复制代码
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
	int l1 = m - 1;
	int 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--];
		}
	}

	//要么是l1 < 0,要么是l2 < 0
	while (l2 >= 0)
	{
		nums1[l3--] = nums2[l2--];
	}
}

3. 顺序表的问题及思考

  1. 中间/头部的插入删除,时间复杂度为O(N)。
  2. 增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

是否存在一种数据结构,能够解决以上顺序表表现出来的问题:

  1. 中间/头部的插入删除,可以一步到位,不需要挪动数据
  2. 不需要扩容
  3. 不会造成空间浪费

链表这种数据结构就可以解决这些问题,我们在下一篇中就会进行介绍。

相关推荐
cwj&xyp22 分钟前
Python(二)str、list、tuple、dict、set
前端·python·算法
Kisorge1 小时前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
晓纪同学3 小时前
QT-简单视觉框架代码
开发语言·qt
威桑3 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服3 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生3 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生3 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb
Java Fans3 小时前
C# 中串口读取问题及解决方案
开发语言·c#
盛派网络小助手3 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#