顺序表(详解)

前言:内容包括:顺序表的概念及其结构,动态顺序表的增删查改函数,初始化函数和销毁函数

目录

顺序表的概念及其结构

静态顺序表:

动态顺序表:

动态顺序表的初始化:SLInit

声明:

定义:

动态顺序表的销毁:SLDestroy

声明:

定义:

动态顺序表的打印:SLPrint

声明:

定义:

动态顺序表的增容:SLCheckCapacity

定义:

动态顺序表的增加元素 - 尾插 SLPushBack

声明:

定义:

动态顺序表的增加元素 - 头插 SLPushFront

声明:

定义:

动态顺序表的删除元素 - 尾删 PopBack

声明:

定义:

动态顺序表的删除元素 - 头删 SLPopFront

声明:

定义:

动态顺序表的查找元素:SLFind

声明:

定义:

动态顺序表的修改元素: SLModify

声明:

定义:

动态顺序表的任意插入元素:SLInsert

声明:

定义:

动态顺序表的任意删除元素:SLErase

声明:

定义:


顺序表的概念及其结构

顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构,一般情况下采用数组
储。在数组上完成数据的增删查改

静态顺序表:

使用定长数组存储元素 ,空间给小了不够用,给大了浪费,不建议使用

动态顺序表:

使用动态开辟的数组存储,现实中都是使用动态顺序表,根据需要动态地分配空间大小

typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;//指向动态开辟的数组
	int sz;//有效数据的个数
	int capacity;//容量空间的大小
}SL;

为了方便某些情况下修改数组元素的类型,我们将数组元素的类型,类型重定义一下(使用typedef),用SLDataType来代表数组元素的类型,比如之前我们在数组中存储的是int类型的元素,现在我们需要存储double类型的元素,则只需要将int 修改为double

typedef double SLDataType;

动态顺序表的初始化:SLInit

声明:

void SLInit(SL* psl);

动态的顺序表是一个结构体,结构体的传参需要传结构体的地址 ,用结构体的指针来接收

不要直接传结构体变量本身

定义:

void SLInit(SL* psl)
{
	assert(psl);//断言 当传参是NULL时报错
	psl->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);//初始时动态开辟4个空间
	if (psl->a == NULL)
	{
		perror("malloc");//动态开辟空间失败,打印错误信息
		return;
	}
	psl->sz = 0;
	psl->capacity = 4;
}

动态顺序表的销毁:SLDestroy

 动态开辟的空间在不使用时需要销毁,归还空间给操作系统

声明:

void SLDestroy(SL* psl);

定义:

void SLDestroy(SL* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->sz = 0;
	psl->capacity = 0;
}

动态顺序表的打印:SLPrint

声明:

void SLPrint(SL* psl);

定义:

void SLPrint(SL* psl)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->sz; i++)
	{
		printf("%d ", psl->a[i]);
	}
}

动态顺序表的增容:SLCheckCapacity

每次在增加元素(插入元素)前需要检查空间的容量,不够就需要增容

定义:

void SLCheckCapacity(SL* psl)
{
	assert(psl);
	if (psl->sz == psl->capacity)
	{
		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * psl->capacity * 2);
		
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		psl->a = tmp;
		psl->capacity *= 2;
	}
}

调整原有空间大小使用realloc函数,一般增容至原来空间的2倍即可

realloc函数调整空间大小:若是原空间后面有足够的空间可以被开辟,则realloc函数会在原空间后追加空间,若是没有足够的空间,则realloc函数会重新找一块足够大的地方,一共开辟2倍的原空间大小,并将原空间里的所有数据拷贝至新空间,销毁原空间,返回新空间的地址

但是realloc是原地开辟还是异地开辟,我们无从知晓,并且realloc开辟可能会失败,返回NULL,因为找不到合适的空间,则我们不能直接用指向原空间的指针去接收realloc的返回值,若是realloc开辟失败,返回NULL,我们又用指向原空间的指针去接收,则我们不仅没有成功扩容,还无法找到原空间的数据了

综上:使用一个指针变量接收realloc的返回值,若是realloc的返回值不为NULL,说明空间开辟成功,再将增容成功后的地址重新赋给指向原空间的指针a

动态顺序表的增加元素 - 尾插 SLPushBack

在插入元素之前需要判断容量,不够就增容,使用之前实现的SLCheckCapacity

声明:

void SLPushBack(SL* psl, SLDataType x);

定义:

void SLPushBack(SL* psl, SLDataType  x)
{
	assert(psl);
	SLCheckCapacity(psl);//检查容量,不够就增容
	psl->a[psl->sz++] = x;
}

尾插就是在数组的尾部插入元素,假设现在数组中已有sz个元素,数组最后一个元素的下标是sz-1,则要尾部插入的元素位置的下标就是sz

动态顺序表的增加元素 - 头插 SLPushFront

在插入元素之前需要判断容量,不够就增容,使用之前实现的SLCheckCapacity

声明:

void SLPushFront(SL* psl, SLDataType  x);

定义:

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	SLCheckCapacity(psl);
	int end = psl->sz - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[0] = x;
	psl->sz++;
}

在首元素的位置插入元素,需要将所有的元素后移一位

动态顺序表的删除元素 - 尾删 PopBack

在删除元素之前需要判断元素个数是否为空,若为空则不用删了

声明:

void SLPopBack(SL* psl);

定义:

void SLPopBack(SL* psl)
{
	assert(psl);
	assert(psl->sz > 0);//元素个数为空会报错
	psl->sz--;
}

动态顺序表的删除元素 - 头删 SLPopFront

在删除元素之前需要判断元素个数是否为空,若为空则不用删了

声明:

void SLPopFront(SL* psl);

定义:

void SLPopFront(SL* psl)
{
	assert(psl);
	assert(psl->sz > 0);
	int start = 0;
	while (start < psl->sz - 1)
	{
		psl->a[start] = psl->a[start + 1];
		start++;
	}
	psl->sz--;
}

删除首元素使用覆盖法

动态顺序表的查找元素:SLFind

声明:

int SLFind(SL* psl, SLDataType x);

定义:

int SLFind(SL* psl, SLDataType x)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->sz; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

若能找到被查找的元素,则返回它的下标,若是找不到则返回-1,因为合法的下标不会为负数

动态顺序表的修改元素: SLModify

声明:

void SLModify(SL* psl, int pos,SLDataType x);

定义:

void SLModify(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	assert(pos >= 0 && pos < psl->sz);
	psl->a[pos] = x;
}

注意:要查找的位置pos必须是有效范围,即0~sz-1

动态顺序表的任意插入元素:SLInsert

在插入元素之前需要判断容量,不够就增容,使用之前实现的SLCheckCapacity

声明:

void SLInsert(SL* psl, int pos, SLDataType x);

定义:

void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	assert(pos >= 0 && pos <= psl->sz);
	SLCheckCapacity(psl);
	int end = psl->sz-1;
	while (end >= pos)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[pos] = x;
	psl->sz++;
}

注意:插入元素的位置必须合理,可以是0~sz,数组元素的存储必须是连续的

比如现在数组的元素个数是10个,pos不能选定为下标为20的位置,因为在下标为20的位置存储一个元素,则会违背数组元素必须连续存储的原理 

SLInsert函数可以用于头插,尾插:

void SLPushBack(SL* psl, SLDataType  x)
{
    assert(psl);
    SLInsert(psl,psl->sz,x);
}
void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	SLInsert(psl,0,x);
}

因为在SLInsert函数内部自会检查空间容量,不够会增容,所以在SLPushBack和SLPushFront函数内部无需去检查空间容量 

动态顺序表的任意删除元素:SLErase

在删除元素之前需要判断元素个数是否为空,若为空则不用删了

声明:

void SLErase(SL* psl, int pos);

定义:

void SLErase(SL* psl, int pos)
{
	assert(psl);
	assert(pos >= 0 && pos < psl->sz);
	int start = pos;
	while (start < psl->sz - 1)
	{
		psl->a[start] = psl->a[start + 1];
		start++;
	}
	psl->sz--;
}

    assert(pos >= 0 && pos < psl->sz):已经包含psl->sz>0的条件

则不用再写 assert(psl->sz > 0)

SLErase函数可用于头删,尾删:

void SLPopFront(SL* psl)
{
	assert(psl);
	SLErase(psl,0);
}
void SLPopBack(SL* psl)
{
	assert(psl);
	SLErase(psl,psl->sz-1);
}

因为 SLErase函数内部自会检查元素个数是否为0,所以在PopBack和PopFront函数内部无需再去判断元素个数是否为空

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值