【数据结构】C语言实现顺序表

文章详细介绍了如何使用C语言实现动态顺序表,包括初始化、销毁、打印、尾插、头插、尾删、头删、插入、删除和查找等功能。同时讨论了顺序表存在的如中间插入删除效率低和空间浪费等问题,并提出单链表作为解决方案。
摘要由CSDN通过智能技术生成


一、顺序表概念

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

顺序表一般可以分为:

  1. 静态顺序表:使用定长数组存储元素。
  2. 动态顺序表:使用动态开辟的数组存储。
// 静态顺序表
// 给小了不够用,给多了浪费
#define N 10000
typedef int SeqListDatatype;
struct SeqList
{
	SeqListDatatype array[N];
	int size;
};
// 动态顺序表
typedef int SeqListDatatype;
typedef struct SeqList
{
	SeqListDatatype* array;
	int size;       // 存储的有效数据的个数
	int capacity;   // 容量
}SeqList;

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

二、顺序表接口定义

// 初始化、销毁
void SeqListInit(SeqList* psl); 
void SeqListDestroy(SeqList* psl);

// 打印顺序表
void SeqListPrint(SeqList* psl);

// 尾插、头插
void SeqListPushBack(SeqList* psl, SeqListDatatype x);
void SeqListPushFront(SeqList* psl, SeqListDatatype x);

// 尾删、头删
void SeqListPopBack(SeqList* psl);
void SeqListPopFront(SeqList* psl);

// 指定位置插入、删除
void SeqListInsert(SeqList* psl, int pos, SeqListDatatype x);
void SeqListErase(SeqList* psl, int pos);

// 查找与修改
int SeqListFind(SeqList* psl, SeqListDatatype x);
void SeqListModify(SeqList* psl, int pos, SeqListDatatype x);

三、顺序表实现

3.1 初始化、销毁与打印

初始化我们预置了4个容量。

void SeqListInit(SeqList* psl)
{
	assert(psl);
	psl->array = (SeqListDatatype*)malloc(sizeof(SeqListDatatype) * 4);
	if (psl->array == NULL)
	{
		perror("malloc fail");
		return;
	}

	psl->capacity = 4;
	psl->size = 0;
}
void SeqListDestroy(SeqList* psl)
{
	assert(psl);
	free(psl->array);
	psl->array = NULL;
	psl->size = 0;
	psl->capacity = 0;
}
void SeqListPrint(SeqList* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->array[i]);
	}
	printf("\n");
}

3.2 尾插pushback

插入之前都要检查容量。

void SeqListPushBack(SeqList* psl, SeqListDatatype x)
{
	assert(psl);

	SeqListCheckCapacity(psl);

	psl->array[psl->size++] = x;
}

3.3 头插pushfront

插入之前都要检查容量。

void SeqListPushFront(SeqList* psl, SeqListDatatype x)
{
	assert(psl);

	SeqListCheckCapacity(psl);

	// 挪动数据
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->array[end + 1] = psl->array[end];
		--end;
	}

	psl->array[0] = x;
	psl->size++;
}

3.4 尾删popback

void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	assert(psl->size > 0);

	psl->size--;
}

3.5 头删popfront

void SeqListPopFront(SeqList* psl)
{
	assert(psl);
	assert(psl->size > 0);

	/*int start = 0;
	while (start < psl->size-1)
	{
		psl->array[start] = psl->array[start + 1];
		start++;
	}*/

	int start = 1;
	while (start < psl->size)
	{
		psl->array[start - 1] = psl->array[start];
		start++;
	}

	psl->size--;
}

3.6 插入Insert-改写头插尾插

插入之前都要检查容量。

void SeqListInsert(SeqList* psl, int pos, SeqListDatatype x)
{
	assert(psl);
	assert(0 <= pos && pos <= psl->size);

	SeqListCheckCapacity(psl);

	int end = psl->size - 1;
	while (end >= pos)
	{
		psl->array[end + 1] = psl->array[end];
		--end;
	}

	psl->array[pos] = x;
	psl->size++;
}
void SeqListPushBack(SeqList* psl, SeqListDatatype x)
{
	assert(psl);

	SeqListInsert(psl, psl->size, x);
}
void SeqListPushFront(SeqList* psl, SeqListDatatype x)
{
	assert(psl);

	SeqListInsert(psl, 0, x);
}

3.7 删除erase-改写头删尾删

void SeqListErase(SeqList* psl, int pos)
{
	assert(psl);
	assert(0 <= pos && pos < psl->size);

	int start = pos + 1;
	while (start < psl->size)
	{
		psl->array[start - 1] = psl->array[start];
		++start;
	}

	psl->size--;
}
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	assert(psl->size > 0);

	SeqListErase(psl, psl->size - 1);
}
void SeqListPopFront(SeqList* psl)
{
	assert(psl);
	assert(psl->size > 0);

	SeqListErase(psl, 0);
}

3.8 查找Find

查找成功返回元素所在下标,查找失败返回-1。

int SeqListFind(SeqList* psl, SeqListDatatype x)
{
	assert(psl);

	for (int i = 0; i < psl->size; i++)
	{
		if (psl->array[i] == x)
		{
			return i;
		}
	}

	return -1;
}

3.9 修改Modify

如果修改位置合法直接修改为指定元素。

void SeqListModify(SeqList* psl, int pos, SeqListDatatype x)
{
	assert(psl);
	assert(0 <= pos && pos < psl->size);

	psl->array[pos] = x;
}

3.10 顺序表测试

int main(int argc, char* argv[]) {
	
	TestSeqList();

	printf("bye bye\n");
	return 0;
}

void TestSeqList()
{
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPushBack(&sl, 6);
	SeqListPrint(&sl); // 1 2 3 4 5 6

	SeqListPopFront(&sl);
	SeqListPrint(&sl); // 2 3 4 5 6

	int pos = SeqListFind(&sl, 6);
	if (pos != -1)
	{
		SeqListErase(&sl, pos);
	}
	SeqListPrint(&sl); // 2 3 4 5

	SeqListDestroy(&sl);
}

四、顺序表存在的问题

问题:

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

如何解决以上问题呢?单链表

五、源码

gitee地址

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

shlyyy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值