C语言实现顺序表

1. 顺序表定义

先声明:顺序表是一种思想而不是一个程序,变量名及实现方法都是不是固定的。

通过实现顺序表以及对其进行操作的各种函数,我们能更方便地对数据进行管理。

相比于数组,静态的顺序表多了一个参数size来记录数组中有效数据的个数。

#define MAX 100
typedef int SLDataType;

struct SeqList
{
    SLDataType data[MAX];
    int size;//有效数据个数
};

除了静态的顺序表之外,还有一种动态的顺序表。

相比于静态的顺序表,动态的顺序表将数组改为了一块动态申请的空间。

这样,我们顺序表的大小就可以随情况而变化,更加灵活。

那么,既然我们使用的是一块动态开辟的空间,我们就还需要一个变量capacity来记录这块空间的大小。

struct SeqList
{
	SLDataType* data;
	int size;//有效数据个数
	int capacity;//容量
};

在实际使用过程中,常采用如下的定义方式:

typedef struct SeqList
{
	SLDataType* data;
	int size;//有效数据个数
	int capacity;//容量
}SL;

一般来说,我们更推荐使用动态的顺序表。

动态的顺序表相比于静态顺序表,其容量可变的特性使得其不会出现表满而无法插入元素的现象,在元素较少时,也不会造成空间的浪费。

需要实现来对顺序表进行操作的函数有 :

//顺序表初始化
void SLInit(SL* ps);
//顺序表的销毁
void SLDestroy(SL* ps);
//检查扩容
void CheckCapacity(SL* ps);
//打印
void SLPrint(SL s);
//尾部插入
void SLPushBack(SL* ps, SLDataType x);
//头部插入
void SLPushFront(SL* ps, SLDataType x);
//尾部删除
void SLPopBack(SL* ps);
//头部删除
void SLPopFront(SL* ps);
//找到某个数据,返回下标
int SLFind(SL* ps, SLDataType x);
//指定位置下标插入
void SLInsert(SL* ps, int pos, SLDataType x);
//指定位置下标删除
void SLErase(SL* ps, int pos);

注意,虽然顺序表是以数组为基础的数据结构,但是其本质上是一个结构体,所以在传参时应采用传址调用。 

2. 顺序表初始化

对结构体中的三个成员分别进行初始化即可。

void SLInit(SL* ps)//初始化
{
	ps->data = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

3. 顺序表销毁

由于动态顺序表的数组是动态开辟的,所以在顺序表停用时,一定要将其销毁(主要是free掉data数组)。

销毁后,表中不再有元素,所以最好将size和capacity都置为0。

void SLDestroy(SL* ps)//销毁
{
	if (ps->data)
	{
		free(ps->data);
	}
	ps->data = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

4. 检查扩容

当表的容量不够时,我们无法对在表中插入数据。

所以,在插入数据之前,一定要先检查表是否已满(size==capacity)。

若表满,则对表进行扩容操作,一般来说,将表的容量扩充到原来的两倍较为合理。

int newcapacity = ps->capacity * 2;

但问题是,刚刚初始化好的表的capacity为0,按上面这样写就无法对刚初始化好的表,或者被销毁过的表进行扩容,所以我们想到下面这样的写法。

int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

接着,用realloc函数对data数组进行扩容。

SLDataType* tmp = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * newcapacity);

由于realloc函数可能扩容失败(返回NULL),于是我们先创建一个新的变量来接收返回值,确认扩容成功之后,在将其赋值给data。

void CheckCapacity(SL* ps)//检查扩容
{
	if (ps->size == ps->capacity)
	{
		//通常来说是成倍的增加
		//一般用2或3倍较为合理
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(-1);
		}
		//空间申请成功
		ps->data = tmp;
		ps->capacity = newcapacity;
	}
}

5. 打印顺序表

到上一步,我们的准备工作就基本完成了,接下来,就可以开始实现对顺序表进行增删查改的函数的。

但是,在写这些函数时,我们希望能够看看表中的数据是否能被这些函数正确操作。

于是在此之前,我们先来实现一个可以打印顺序表的函数。

void SLPrint(SL s)//打印
{
	for (int i = 0; i < s.size; i++)
	{
		printf("%d ", s.data[i]);
	}
	printf("\n");
}

6. 尾插

顾名思义,就是在顺序表的末尾插入数据。

void SLPushBack(SL* ps, SLDataType x)//尾插
{
	if (ps == NULL)
	{
		return;
	}
	//assert(ps);也可
	CheckCapacity(ps);
	ps->data[ps->size] = x;
	ps->size++;
}

7. 头插

顾名思义,就是在顺序表的开头插入数据。

相比于尾插,这个操作就要麻烦的多,因为在头部插入数据之前,我们要先将原有的数据全部向后移动一格。

void SLPushFront(SL* ps, SLDataType x)//头插
{
	if(ps == NULL)
	{
		return;
	}
	CheckCapacity(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->data[i] = ps->data[i - 1];
	}
	ps->data[0] = x;
	ps->size++;
}

8. 尾删

顾名思义,就是将顺序表尾部的数据删除。

这一步,其实只需要让size自减1即可,这样一来,最末尾上的数据就不算有效数据了。

void SLPopBack(SL* ps)//尾部删除
{
	assert(ps);
	if(ps->size > 0)//也可以是assert(ps->size)
	ps->size--;
}

9. 头删

顾名思义,就是将顺序表头部的数据删除。

与头插相反,这里我们需要让所有数据全部向前移动一格,然后让size--,第一个数据在这个过程中就被第二个数据覆盖掉了。

void SLPopFront(SL* ps)//头部删除
{
	assert(ps);
	assert(ps->size);
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}

10. 查找某个元素

int SLFind(SL* ps, SLDataType x)//找到某个数据,返回下标
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == x)
			return i;
	}
	return -1;
}

11. 在指定位置插入数据

与头插类似,先给新数据腾出位置,然后再进行插入。

只要是插入,都需要先检查是否需要扩容。

void SLInsert(SL* ps, int pos, SLDataType x)//指定位置下标插入
{
	assert(ps);
    assert(pos >= 0 && pos <= ps->size);
	CheckCapacity(ps);
	for (int i = ps->size; i > pos; i--)//给新数据挪位置
	{
		ps->data[i] = ps->data[i - 1];
	}
	ps->data[pos] = x;
	ps->size++;
}

12. 删除指定位置数据

与头删类似。

void SLErase(SL* ps, int pos)//指定位置下标删除
{
	assert(ps);
    assert(pos >= 0 && pos <= ps->size - 1);
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}

13. 总结

代码仅作参考,顺序表是一种思想而不是固定的程序或代码,只要能达到相同的目的即可。

其细节要依据实际要处理的问题来进行修改。

实战:C语言实现学生管理系统-CSDN博客

  • 40
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大筒木老辈子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值