C语言实现顺序表

一 顺序表的定义

1 概念

  顺序表是一段连续物理地址存储单元存数据元素的线性结构(在逻辑上是连续存储的就是线性表:比如链表 栈 队列等)。一般情况下用数组存储,在数组上完成数据的增删查改。

  连续的物理地址说明每个元素再内存中时挨个存放着的。这一点有别于链表。

图示:

 

二 顺序表的操作

①结构

  为了方便顺序表能够动态存储数据(扩容),因此如此定义顺序表的结构:

用一个结构体,结构成员分别设置一个指针、一个size和一个capacity

·指针:指向这一块连续存储的空间

·size:存储的数据的个数

·capacity:实际能存储元素的多少

size和capacity:为了实现动态效果,size和capacity通常配合使用

//定义顺序表的结构
typedef int SLDataType;//存储的数据类型 假设是int类型的数据
typedef struct SequenceList
{
	SLDataType* a;//动态开辟
	int size;//数组的大小
	int capacity;//数组的容量 方便进行容量方面的检查
}SL;//取名方便后续使用

关于typedef的两个用途:

a.方便后续更改存储的数据的类型

b.取别名,方便使用

②操作

1 初始化

注意:涉及到解引用操作的要对指针进行判断。防止对无意义的指针进行解引用。

这里初始化capacity先设置成0,也可以设置成指定大小。

void InitSL(SL* sl) {
	assert(sl);//由于后续需要解引操作 先进行判定 该指针不能为空

	sl->a = NULL;
	sl->size = sl->capacity = 0;
}

 

2 销毁

主要是为了进行内存清理


void DestroySL(SL* sl){
	assert(sl);//涉及对指针解引用都判断一下
	//避免对空指针的再次释放
	if (sl->a) {
		free(sl->a);
		sl->a = NULL;
		sl->size = sl->capacity = 0;
	}
}

3 打印

遍历即可。主要是为了test

void PrintSL(SL* sl){
	assert(sl);

	for (int i = 0; i < sl->size; i++) {
		printf("%d ", sl->a[i]);
	}
	printf("\n");
}

  由于插入操作有可能涉及到一个问题:容量不够。因此就需要对容量进行检查。由于后续不管头插尾插还是任意位置插入都需要进行检查,因此把这个功能抽象成一个接口函数方便复用。

void CheckCapacity(SL* sl) {
	//容量检测
	//如果插入之后 存储的元素大于数组的容量 重新进行定义开辟(size==capacity说明再插入就越界了)
	if (sl->size == sl->capacity) {
		int newCapacity = sl->capacity == 0 ? 4 : 2 * sl->capacity;//1 一开始初始化容量为0 2:后续容量不够
		SLDataType* tmp = (SLDataType*)realloc(sl->a, newCapacity * sizeof(SLDataType));
		assert(tmp);//1 原地扩容 2 异地扩容 3 失败返回NULL 差错处理

		//将sl重新赋值
		sl->capacity = newCapacity;
		sl->a = tmp;
	}
}

 要进行扩容操作的话需要对capacity和a重新进行赋值。

newCapacity为新扩容之后的容量大小:需要考虑到一开始初始化为0以及后续扩容的情况。我们扩容一般是二倍扩容,如果一开始是0的情况没有考虑的话,两倍0是没意义的

tmp为新扩容之后的地址空间:需要用realloc进行扩容,有这几个点需要注意。第一个参数需要传入需要进行扩容的地址空间;第二个参数需要传入重新定义大小的字节数;返回值是void*需要进行转换。需要判断扩容是否失败

4 头插

 数据整体都要往后移动一个位置,并且是从后往前进行移动的。

开始的位置是size-1(下标比元素个数小一个,最开始的下标为0的位置也需要移动)

void PushFrontSL(SL* sl, SLDataType x) {
	assert(sl);

	CheckCapacity(sl);

	//数组整体往后移动一个位置
	for (int i = sl->size - 1; i >= 0; i--) {
		sl->a[i + 1] = sl->a[i];
	}
	//插入新的元素
	sl->a[0] = x;
	sl->size++;
}

5 尾插

void PushBackSL(SL* sl, SLDataType x) {
	assert(sl);

	CheckCapacity(sl);

	//插入
	sl->a[sl->size] = x;
	sl->size++;
}

6 任意位置插入

  和头插移动的思路有点类似,不过由于是pos位置插入,因此需要将pos连同之后的元素整体往后移动一个位置

void InsertSL(SL* sl, SLDataType x,int pos) {
	assert(sl);
	assert(pos >= 0 && pos <= sl->size);
	CheckCapacity(sl);
	//移动
	for (int i = sl->size - 1; i >= pos; i--) {
		sl->a[i + 1] = sl->a[i];
	}
	//插入
	sl->a[pos] = x;
	//更新size
	sl->size++;

}

7 头删

头删需要考虑的情况比较复杂

对于删除,都是要求有元素才可以进行删除。如果size为0就不能删除了

除此之外,是移动进行删除的,那么size为1 的位置需要进行特殊的处理

就是移动进行删除,但是删除的话是从前往后移动数据进行删除

void PopFrontSL(SL* sl) {
	assert(sl);
	assert(sl->size > 0);//只有大于0才能进行删除
	//处理为1的情况
	if (sl->size == 1) {
		sl->size--;
		return;
	}
	for (int i = 1; i < sl->size; i++) {
		sl->a[i-1] = sl->a[i];
	}
	sl->size--;
}

8 尾删

尾删比较简单

void PopBackSL(SL* sl) {
	assert(sl);
	assert(sl->size > 0);

	sl->size--;
}

9 任意位置删除

任意位置删除的话,需要注意pos的范围。

void EraseSL(SL* sl,int pos) {
	assert(sl);
	assert(pos >= 0 && pos <= sl->size - 1);

	for (int i = pos; i < sl->size; i++) {
		sl->a[i] = sl->a[i + 1];
	}

	sl->size--;
}

10 查找某一元素在顺序表中是否存在

遍历即可

int FindSL(SL* sl, SLDataType x) {
	assert(sl);

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

11 对顺序表某一位置的数据进行修改

void ModifySL(SL* sl, SLDataType x, int pos) {
	assert(sl);
	assert(pos >= 0 && pos <= sl->size);

	sl->a[pos] = x;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值