前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >顺序表操作详解

顺序表操作详解

作者头像
用户11029129
发布2024-06-04 13:03:24
690
发布2024-06-04 13:03:24
举报
文章被收录于专栏:编程学习

一、顺序表结构定义

数组可以存储数据,而对数组的数据进行操作,例如增删改查等操作被称为顺序表,顺序表需要大量用到C语言的结构体与指针,我们先来想想,如果想要对一个数组进行数据操作,比如插入元素操作,首先肯定是需要一个数组来存储数据的,那么对于要插入位置的索引是不是还需要一个角标,用来记录元素的个数,在进行元素索引的时候以便于快速找到。

代码语言:javascript
复制
typedef struct vector {//结构体定义
	int size, count;//size为数组大小,count为数组存储元素个数
	int* data;//data为数组
}vector;//重命名为vector

二、顺序表初始化

在我们使用这个顺序表之前,需要先初始化顺序表一下,相信你也看到了,结构体内采用的存储方式为指针,那么就意味着在初始化的时候需要对指针进行malloc处理。

代码语言:javascript
复制
vector* InitNewVector(int n)//结构体初始化,n为开辟空间大小
{
	vector* v = (vector*)malloc(sizeof(vector));//开辟这个结构体
	v->size = n;//记录空间大小为n
	v->count = 0;//此时数组内部没有元素
	v->data = (int*)malloc(sizeof(int) * n);//动态数组开辟空间
	return v;//返回开辟好的结构体
}

顺序表的初始化操作我们就完成了,这个时候你已经拥有了一个顺序表,只不过这个时候顺序表内还没有元素,那么接下来我们就需要实现数据结构的基本操作了,增删改查。

三、内存释放

首先,既然创造了这个顺序表是由堆里面开辟的,那么就需要开发者自己手动的来回收这些内存,接下来来实现一下顺序表销毁操作:

代码语言:javascript
复制
void clearVector(vector* v)//销毁顺序表,传入顺序表指针
{
	if (v == NULL)
		return;
	free(v->data);//先free掉顺序表内部动态开辟的数组
	free(v);//再将顺序表给销毁
	return;
}

值得注意的是,在销毁顺序表需要由内而外销毁,如果直接销毁整个顺序表并不会自动帮你把内部数组销毁,反而这样会让你丢失对应的地址在想要释放内部数组就很困难了。

四、插入操作

接下来进行顺序表的插入操作,在实现操作之前,你需要知道再插入之前的特别情况是什么, 如果传入函数的位置不对,或者顺序表内部数组元素(count)个数大于了数组大小(size)是属于错误情况的,还有一种重要的情况是当数组元素满了(size == count)的时候也是需要返回。

代码语言:javascript
复制
int insert(vector* v, int pos, int val)//插入操作,对pos位置插入val值
{
	if (v->size < pos || pos < 0) return 0;//排除错误情况
	if (v->size == pos)//当数组满了也需要进行判断
		return 0;//返回0代表插入失败
	for (int i = v->count - 1; i >= pos; i--)//数组进行插入前需要将要插入位置之后的数据都往后移动一个单位
		v->data[i + 1] = v->data[i];//向后移动
	v->data[pos] = val;//此时pos位置的值已经被空下来了直接插入就行
    v->count += 1;//插入完之后数组元素个数进行记录一下
	return 1;//返回1代表插入成功
}

五、删除操作

顺序表插入操作已经完成了, 接下来实现元素的删除操作,同插入相似,删除的位置如果小于0或者大于size都返回0。

代码语言:javascript
复制
int delete(vector* v, int pos)//传入结构体指针与要删除的位置
{
	if (v == NULL) return 0;
	if (pos < 0 || v->size < pos)//排除边界情况
		return 0;
	for (int i = pos + 1; i < v->count; i++)//进行元素删除
		v->data[i - 1] = v->data[i];
	v->count -= 1;//删除完元素个数减一
	return 1;//删除成功返回1
}

删除,插入操作已经实现出来,查找操作非常简单可以自己尝试去编码一下,这里就不详细展示了。

六、实现随机插入删除

接下来便是如何把数据进行体现出来,在这里我采用随机插入随机删除的方法进行代码演示,原理就是状态码进行分发,在接收任务时进行概率分配任务,详细如下:

代码语言:javascript
复制
int main()
{
	srand((unsigned int)time(NULL));//产生伪随机
	vector* v = InitNewVector(2);//初始化生成顺序表

    #define MAX_OP 10
	for (int i = 0; i < MAX_OP; i++)//进行数据操作的此数
	{
		int pos, val, op = rand() % 5;//pos为位置,val为待插入值,op为概率分配任务,在0-4之间分配任务随机产生数字
		switch (op)//经过op分发任务删除概率为20%,插入概率为80%
		{
	    	case 0:
		    {
			    pos = rand() % (v->count + 1);//插入位置进行随机
    			printf("Delete item %d at vector! = %d\n"
	    		, pos, delete(v, pos));
		    	break;
    		}
	    	case 1:
		    case 2:
    		case 3:
	    	case 4:
		    {
    			pos = rand() % (v->count + 1);
	    		val = rand() % 100 + 1;
		    	printf("Insert %d to %d at vector! = %d\n"
			   	, val, pos, insert(v, pos, val));
    			break;
	    	}
		}
		output(v);//打印函数,最后每次插入都会进行打印
	}
	clearVector(v);//销毁顺序表
	return 0;
}

伪随机产生随机数,在进行状态码分发方式进行对插入删除操作进行任务分配,最后输出顺序表中的内容。

七、输出方式

为了使顺序表输出方式更加美观,我采用自己的一种常用输出方式:

代码语言:javascript
复制
void output(vector* v)//顺序表输出操作
{
	if (v == NULL)
		return;

	int len = 0;
	for (int i = 0; i < v->count; i++)
	len += printf("%3d", i);

	printf("\n");
	for (int i = 0; i < len; i++)
	printf("-");

	printf("\n");
	for (int i = 0; i < v->count; i++)
	printf("%3d", v->data[i]);

	printf("\n");
	printf("\n");
	printf("\n");

	return;
}

这样输出结果为:

这样是不是就美观一些,当然有艺术细胞的同志可以自行设计。

八、插入操作改变以及扩容操作

现在有个新的问题,如果顺序表满了,那该怎么办?难道在写一份顺序表吗?答案是否定的,如果你C语言学了realloc这个函数,那么你扩容的问题就可以简单解决 。首先,要思考是在什么情况下才需要进行扩容,在哪步操做下需要扩容?没错,当插入元素满了的时候进行扩容操作,所以可以这样设计插入函数:

代码语言:javascript
复制
int insert(vector* v, int pos, int val)
{
	if (v->size < pos || pos < 0) return 0;
	if (v->size == pos  && !expand(v))//在这里进行扩容判断,如果扩容成功则继续插入操作否则return 0
		return 0;

	for (int i = v->count - 1; i >= pos; i--)
		v->data[i + 1] = v->data[i];

	v->count += 1;
	v->data[pos] = val;

	return 1;
}

那么该如何实现扩容函数呢?其实很简单,用一个整形指针变量接收realloc后的值,在进行判断是否扩容失败,如果成功则把这个变量的值赋给结构体的数组,这里realloc的值可以自行调整大小,我这里默认扩容两倍大小。

代码语言:javascript
复制
int expand(vector* v)
{
	if (v == NULL)
		return 0;

	int *tmp = (int *)realloc(v -> data, sizeof(v->size) * 2 * sizeof(int));//指针变量接收realloc后的值防止扩容失败造成崩溃
	if (tmp == NULL)//对realloc函数是否扩容成功进行检查
	{
		perror("realloc");
		return 0;
	}
	v->data = tmp;
		
	printf("Expand %d from %d at vector!\n", v->size, v->size * 2);//扩容成功进行提示
	v->size *= 2;//将容量进行相同倍数扩大

	return 1;//成功返回1
}

这样输出的结果为:

可以看到确实发生了扩容操作,这样一个完整的顺序表就实现出来了!如果有帮到你的话还望三连多多支持作者~~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档