【通讯录项目 (2 / 3)】基于顺序表的通讯录实现——顺序表功能实现

基于顺序表的通讯录实现——顺序表功能实现


经过上一篇文章我们对顺序表有了一个初步的认识,下面我们将通过C语言实现顺序表的功能,包括: 增加数据 删除数据 查找数据 修改数据
可以把顺序表看作一种特殊的数组,我们下面将要进行的操作是基于
数组 数组操作 动态内存管理等基本功能实现的

顺序表功能

1 初始化与销毁

顺序表底层这里我们用“ SLDataType”来代替传统的int char等关键字,这样以后,就可以避免在修改变量类型的时候,进入"地狱模式"。

1.1 初始化

void SLInit(SL* ps)
{
	assert(ps);//断言检测是否为空指针
	ps->a = NULL;//初始化
	ps->size = ps->capacity = 0;//初始化
}

注意这里我们使用的是“传址操作”,这样可以在原有基础上修改数据信息。

1.2 销毁

void SLDestroy(SL* ps)
{
	assert(ps);//断言检测是否为空指针
	free(ps->a);//释放顺序表数据空间
	ps->size = ps->capacity = 0;//销毁归零
	ps->a = NULL;//避免“野指针”
}

注意这里我们使用的是“传址操作”,这样可以在原有基础上修改数据信息。

初始化和销毁的操作比较简单,不在做详细阐释。观察代码即可。

2 头部插入与删除

2.1 头部插入

void SLPushFront(SL* ps, SLDataType x)
 {
	assert(ps);//断言检测是否为空指针
	//判断空间是否足够,不够则扩容
	SLCheckCapacity(ps);
	//空间足够,历史数据后移一位
	for (size_t i = ps->size; i > 0; i--){
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[0] = x;
	ps->size++;
}

这里我们插入的时候需要考虑空间是否足够
于是我们单独分装一个函数进行容量判断

2.1.1检查容量
void SLCheckCapacity(SL* ps)
{

	if (ps->size == ps->capacity){
		//三目操作符,为零设为 4,反之为原来空间的2倍
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		//开辟空间
		SLDataType* tmp = (SLDataType*)realloc(ps->a,newcapacity*sizeof(SLDataType));
		//检查开辟是否成功
		if (tmp == NULL){
			perror("realloc fail!");
			return;
		}
		ps->a = tmp;//转移新空间
		ps->capacity = newcapacity;//设置新容量                                           
	}
}

我们一行一行的解读
1 首先判断原顺序表是否为空,如果为空则设置为四个容量,反之为原来二倍
倍数原则上可以设置为任意大小,这里之所以设置为二倍,是因为这样利用率最高
2 然后开辟空间
我们重温一下realloc函数
在这里插入图片描述
注意“size”代表新的空间大小,而不是需要增加的空间大小
开辟完空间即可进行对顺序表的扩容

2.1.2 插入数据

在这里插入图片描述
头部插入只需在检查容量之后,依次移动顺序表数据即可,再将数据插入到头部即可,类似数组操作。
插入完成之后不要忘记将 size 加 1

2.2 头部删除

void SLPopFront(SL* ps)
{
	assert(ps);//断言检测是否为空指针
	assert(!SLIsEmpty(ps));//断言检测指针指向是否为空数据。
	for (int i = 0; i < ps->size - 1 ; i++)
	{
		ps->a[i] = ps->a[i + 1]; // a[size-2]=a[size-1] (模拟最后一次操作)
	}
	ps->size--;
}

头部删除非常简单,将顺序表中的数据从第二个依次向前覆盖即可。

2.2.1 检测数据是否为空
bool SLIsEmpty(SL* ps) {
	assert(ps);
	//这样子是不对的,这里只能判断空间是否足够
	//return ps->size == ps->capacity;
	return ps->size == 0;
}

使用断言即可,切记不要用“ps->size == ps->capacity”这样只能判断空间是否足够

3 尾部插入与删除

尾部操作相对于头部更加简单

3.1 尾部插入

void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);//断言检测是否为空指针
	SLCheckCapacity(ps);//判断空间是否足够,不够则扩容
	ps->a[ps->size++] = x;
}

检查空间之后,进行赋值即可。

3.2 尾部删除

void SLPopBack(SL* ps)
{
	assert(ps);//断言检测是否为空指针
	assert(!SLIsEmpty(ps));//断言检测指向是否为空数据。
	ps->size--;
}

在断言并检测数据之后,将size减1即可,这里不需要将删除的数据进行处理,避免出现数据类型的不符合。

4 指定位置插入与删除

指定位置的插入与删除需要一步“查找”目标数据操作,才可以进行精准操作。
当然也可以不进行“操作”,直接对位置进行修改。

4.1 指定位置插入

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);//断言检测是否为空指针
	SLCheckCapacity(ps);//判断空间是否足够,不够则扩容
	if (pos >= 0 && pos < ps->size)//防止越界操作
	{
		for (int i = ps->size; i > pos; i--)
		{
			ps->a[i] = ps->a[i - 1];//a[pos+1]=a[pos]
		}
		ps->a[pos] = x;
		ps->size++;
	}
	else
		assert(pos);//越界进行停止
}

pos是要修改的位置,再检测容量和检测pos是在合理范围内之后,即可进行移动插入操作。
如果要精准插入到一个数据之前则需要对pos进行操作。

4.1.1 查找数据
    pos = SLFind(SL, 5)//举例 “5”
//函数内部
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);//断言检测是否为空指针
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
			return i;//找到返回偏移量 i 
		else
			continue;
	}
	return -1;//没找到
}

这样便可以进行准确插入。

4.2 指定位置删除

void SLErase(SL*ps,int pos)
{                                                                                          
	assert(ps);//断言检测是否为空指针
	if (pos >= 0 && pos < ps->size)
	{
		for (int i = pos; i < ps->size - 1; i++)
		{
			ps->a[i] = ps->a[i + 1];  //a[size-2]=a[size-1](模拟最后一次操作)
		}
		ps->size--;
	}
	else
		assert(pos);
}

删除较为简单,对pos位置进行覆盖操作即可。注意size减1。

5 打印顺序表

打印操作十分简单,与打印数组类似。

5.1 打印顺序表

void SLPrint(SL* ps)
{
	assert(ps);//断言检测是否为空指针
	int i = 0;
	//依次打印数据即可
	for (i = 0; i < ps->size; i++)
	{
		printf("%2d", ps->a[i]);
	}
	printf("\n");
}

由于我们这里展示的是最简单的顺序表。所以打印十分简单。

6 结束语

顺序表的功能我们已经实现,我们使用的是最简单的顺序表,所以整个过程看起来没有困难。在下一篇文章中我们将进行通讯录的实现。
在通讯录里,顺序表的类型不在是简单的" int ",而是结构体类型。
下面给出通讯录的基本功能供大家参考预习。
在这里插入图片描述

期待下一次与你见面!!!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我龙翔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值