数据结构之顺序表详解

news2024/12/26 20:44:47

在这里插入图片描述

hello,大家好,今天的内容是关于顺序表的,其实之前也发过文章,但是那个时候水平还是差了一点,有些地方不是很详细,这次会把每个点都讲清楚,也当给自己在复习一遍。

顺序表在本质上就是数组,顺序表是连续的,我们的数组在内存上也是连续存储的,所以我们可以认为他们在物理结构上是一样的。

1.线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储

简单来讲其实线性表就是零个或者多个元素的有限序列。

线性表有链表和顺序表,我们今天来讲的就是最简单的顺序表。

2.顺序表

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

顺序表也可以分为动态顺序表和静态的顺序表,静态的意义不是很大,使用场景受到权限,所以我们这里一般用动态顺序表。我们今天讲的是动态的,但是也会提到静态是怎么个样子,会写动态的,静态的肯定也会。

如果我们要定义一个静态的话。

在这里插入图片描述

这个就是我们静态的顺序表的结构体,但是静态的是存在缺陷的,比如我们如果要存9个数据,这样就存不下来,如果我们这个N给的太大,我们假设这里有十万个数据,那我们这来存一万个数据的话,这就是铁铁的浪费,所以我们用动态开辟的方法来实现才是最好的。

动态写的方式

在这里插入图片描述
size就是我们有多少个数据,capacity就是有多是空间,我们这里在进行一些优化,比如我们要存储的是double的类型,我们这里需要改很多东西,结构体的名字太长了,我们也简化一下。

在这里插入图片描述

typedef int SLDateType;
typedef struct SeqList
{
	SLDateType* a;//指向动态开辟的空间
	int size;//数据个数
	int capacity;//容量
}SL;

这里引出一个问题,我们数据结构顺序表中每次开空间都是二倍的开吗?

答案:不是,二倍是一个比较合理的开辟内存大小的倍数,当然我们也可以1.5倍的开辟,数据结构中没有规定一次性开多少空间,只有合理和不合理之说。这里我们开空间就是以二倍的方式进行开辟。

完成了这个之后,我们得先写一个初始化,这个是必须的,我们后面也一定要写初始化,当然在C++中就可以不用,我们有我们的构造函数。

void SLInit(SL psl);

我们这样写初始化是对的吗,我们都知道,在C语言中,形参是实参的一份临时拷贝,改变形参是不会对实参进行改变的,我们要对外面的内容进行修改,就得用指针,所以我们需要传他们的地址。

那正确的写法就是void SLInit(SL* psl);

我们实现一下。

void SLInit(SL* psl)
{
	psl->a = NULL;
	psl->capacity = psl->size = 0;
}

我们初始化其实有两种方法,在初始化的时候就给空间,当然也可以在我们扩容的时候给空间,我们今天这里就不在初始化的时候给空间。
我们会开空间,也就代表我们还需要写一个函数来释放我们动态开辟的空间。

void SLDestory(SL* psl)
{
	if (psl->a != NULL)
	{
		free(psl->a);
		psl->a = NULL;
		psl->capacity = psl->size = 0;

	}
}

那像我们的通讯录是一样的我们对数据进行处理的话,需要增删查改,那这里顺序表也是一样,我们这里就先对顺序表进行尾插。

首先我们要思考,尾插是不是就是在数据的末尾插入一个空间,那我们需要考虑的问题最主要的就是我们的内存空间够吗,比如我们的capacity是4个空间容量大小,我们这个数据个数刚刚好就是4个,那我们还要在插入一个数据的话,是不是满了,这个时候动态顺序表的作用就出来了,我们在开辟二倍的空间进行插入,这个时候我们的空间容量就是8个,在插入的时候就是没有问题。

那我们在尾插的时候是不是就要写一个检查空间的函数。


void SLCheakCapacity(SL* psl)
{
	if (psl->capacity == psl->size)
	{
		int NewCapacity = psl->capacity == 0 ? 4 :psl->capacity * 2;
		SLDateType* tmp = (SLDateType*)realloc(psl->a, sizeof(SLDateType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail\n");
			exit(-1);
		}
		psl->a = tmp;
		psl->capacity = NewCapacity;
	}
}

这里我们其实要注意的有两点,一个就是我们在初始化函数的时候我们是没有进行空间开辟的,所以我们用一个三目操作符来进行判断,还有一个就是realloc的理解问题,大家这里可能多少有点疑问,首先我认为在这里使用realloc的原因一就是当我们的地址第一个参数为空的时候就是malloc,然后因为我们顺序表是连续开辟空间的,所以用realloc更好,realloc的特点是如果后面的空间是足够的,我们之间原地扩容,但是如果后面的空间是不够的,那这个时候我们就会采取另一种方式,异地扩容,找一个空间大的,然后扩容,并返回该地址,还会释放之前的空间,所以realloc更好。

那我们实现检查空间后,我们尾插是怎样的呢,我们都知道顺序表的本质就是数组,所以顺序表的尾插就可以相当于在数组末尾插入一个数据。

void SLPushBack(SL* psl, SLDateType x)
{
	SLCheakCapacity(psl);
	psl->a[psl->size] = x;
	psl->size++;
}

那我们可以在写一个printf的函数来看我们的结果,当然也可以通过其他调试窗口来看我们的结果,这里我们就简单一点,通过我们打印来看。

打印函数

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

测试代码

int main()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);
	SLPrint(&s);
	return 0;
}

测试结果

在这里插入图片描述
尾插写完之后,我们就马上来写我们的头插,头插就是在我们的数组下标为0的位置进行插入,那我们要保持后面的数据整体往后移动,整体移动的时间复杂度就是O(N),所以这里的问题就是头插的效率慢。那插入数据就要看空间是不是满足,所以扩容之前我们一定要检查空间是不是够的。

void SLPushFront(SL* psl, SLDateType x)
{
	SLCheakCapacity(psl);
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[0] = x;
	psl->size++;
}

我们再来测试一下看看结果和测试代码

测试代码

#include"Seqlist.h"
void test1()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);
	SLPrint(&s);
	SLPushFront(&s, 10);
	SLPushFront(&s, 20);
	SLPushFront(&s, 30);
	SLPushFront(&s, 40);

	SLPrint(&s);

}
int main()
{
	test1();

	return 0;
}

在这里插入图片描述
我们后面马上来实现尾插是怎样的,尾插这里我们主要考虑两个问题,一个是删完了怎么办,还有一个就是删的这个位置怎么处理。

void SLPopBack(SL* psl)
{
	assert(psl->size > 0);
	psl->size--;
}

其实仔细想想就这一点代码,如果我们的元素只有0个的时候,我们就不能在对顺序表进行删除,所以我们这里要的就是断言一下,还有就是我们因为后面操作其实会覆盖,那我们就不需要考虑这么多,直接删除就完事了。
我们来测试看看。

在这里插入图片描述
测试代码

void test2()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPrint(&s);
	SLPopBack(&s);
	SLPopBack(&s);
	SLPopBack(&s);
	SLPrint(&s);

}
int main()
{
	//test1();
	test2();

	return 0;
}

那如果我们再删两个看看。
在这里插入图片描述
就被提醒了,所以这里断言我们就暴力一点,当然也可以温柔一点的给个if,

在这里插入图片描述
在这里小编推荐暴力一点的写法。

就直接用assert

这里我们可以看到其实我们尾删和尾插都是特别高效的,但是头插入和头删除,因为我们要移动数据,所以变得不是特别高效

下一个就是怎么实现我们得头删,首先啥都不管,我们先要考虑得就是我们不能为空得顺序表,一个数据都没有我们也就不要再继续删了,其次我们删除头部得数据,但是还有保持其他数据是按顺序的,所以这里我们就要考虑覆盖的问题。

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

测试代码和结果

void test2()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	
	SLPrint(&s);
	SLPushFront(&s, 10);
	SLPushFront(&s, 20);
	SLPushFront(&s, 30);
	SLPushFront(&s, 40);
	SLPrint(&s);
	SLPopFront(&s);
	SLPopFront(&s);
	SLPrint(&s);
}
int main()
{
	//test1();
	test2();

	return 0;
}

在这里插入图片描述
下面我们还有一个随机插入和随机删除,这两个的插入删除我们就需要注意一点东西了,比如我们插入和删除是不是还是要保持顺序表是线性结构,是线性结构我们就需要他们保持连续,所以一些位置的删除和插入需要我们注意,还有就是我们还需要主要随机插入和删除覆盖数据的顺序,是从前覆盖还是从后覆盖,这都需要我们注意,那我们写写一个随机插入吧。

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

}

自信的男人不需要测试(这里大家还是需要不断进行调试的,因为如果我们后面出错的话很麻烦,如果你水平很高的话,当我没说,小编是弱鸡)

那我们这里也来测试和调试一下吧

在这里插入图片描述
测试代码

void test2()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	
	SLPrint(&s);
	SLPushFront(&s, 10);
	SLPushFront(&s, 20);
	SLPushFront(&s, 30);
	SLPushFront(&s, 40);
	SLPrint(&s);
	SLPopFront(&s);
	SLPopFront(&s);
	SLPrint(&s);
	SLInsert(&s, 1, 100);
	SLInsert(&s, 1, 100);
	SLInsert(&s, 0, 100);
	SLPrint(&s);
}

还有一个就是随机删除。

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

随机删除就要看数据有没有,是不是顺序表为空,还要判断我们删除的位置是不是正确,我们不能在psl->size位置进行删除,这个大家可以通过画图来思考,下面我们在写一个查找,查找其实就是遍历数组.

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

找到了我们就返回当前的位置,没找到就返回-1。

那今天我们的学习就到这里了,我们后面接着干,我要补作业了家人们。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1150712.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【智能座舱系列】- 深度解密小米Hyper OS,华为HarmonyOS区别

上一篇文章《小米的澎湃OS到底牛不牛?与鸿蒙系统之间差距有多大》,从多个方面比较了小米Hyper OS 与 华为HarmonyOS的区别,本篇文章继续从架构层面深度解读两者本质的区别。 小米澎湃OS是“以人为中心,打造人车家全生态操作系统”,该系统基于深度进化的Android以及自研的V…

SpringMVC Day 07 : 表单验证

前言 表单验证在Web开发中是非常常见和重要的一部分&#xff0c;它用于确保用户提交的数据符合预期的规则和限制。 通过表单验证&#xff0c;我们可以有效地捕获并处理用户输入中的错误或不正确的数据&#xff0c;从而提高应用程序的数据质量和用户体验。在本教程中&#xff…

Gerrit 事件监听实现

环境 Centos 7.9 Gerrit 2.15 Gerrit 2.15容器搭建 docker-compose.yml version: 3 services:gerrit:image: gerritcodereview/gerrit:2.15ports:- 8080:8080- 29418:29418volumes:- ./review_site:/var/gerrit/review_siteenvironment:- CANONICAL_WEB_URLhttp://localhos…

.jnlp

首先配置电脑的java环境。 百度搜索jre下载&#xff0c;会有很多结果&#xff0c;一般选择官网进行下载。 下载正确的jre版本。 我的电脑是windows 64位&#xff0c;根据你自己电脑的情况选择版本进行下载。不懂自己电脑是多少位的可以看下一步。 查看电脑是64位还是32…

并行和并发有什么区别?

并行和并发 并行和并发最早其实描述的是 Java 并发编程里面的概念。他们强调的是 CPU 处理任务的能力。简单来说&#xff1a; 并发&#xff0c;就是同一个时刻&#xff0c;CPU 能够处理的任务数量&#xff0c;并且对于应用程序来说&#xff0c;不会出现卡顿现象。并行&#x…

如何改善设备综合效率(OEE)并提高工厂的生产力

在现代制造业中&#xff0c;提高设备综合效率&#xff08;Overall Equipment Efficiency&#xff0c;OEE&#xff09;是企业追求高效生产和优化生产能力的重要目标之一。OEE是一个关键的绩效指标&#xff0c;可以帮助企业评估设备的利用效率、生产效率和质量水平。本文将从三个…

ERROR: There can be only one Game target per project.

UATHelper: Packaging (Windows (64-bit)): ERROR: There can be only one Game target per project. D:\dock\Intermediate\Source 把旧的文件删去 一般会出现在更改项目名称后 感谢 There can be only one Game target per project - Development Discussion / Content C…

YOLOv8修改特征金字塔(替换SPPF模块)

1.引言 1.1 引言 修改特征金字塔模块&#xff0c;即SPPF模块是YOLOv8改进中非常常见的一个改进点。 以下将介绍如何在yolov8中修改SPPF模型。 2.2 常见特征金字塔模块 常见特征金字塔可以看此贴&#xff1a;常见特征金字塔模块代码实现 1.3 本文示例 本文使用SimSPPF模块…

JAVA 学习笔记——抽象类

概念&#xff1a; 当定义一个类时&#xff0c;常常需要定义一些成员方法来描述类的行为特征&#xff0c;但有时这些方法的实现方式是无法确定的。 例如&#xff0c;前面在定义 Animal 类时&#xff0c;walk()方法用于描述动物的行走行为&#xff0c;但是针对不同的动物&#…

用友 GRP-U8 存在sql注入漏洞复现

0x01 漏洞介绍 用友 GRP-U8 license_check.jsp 存在sql注入&#xff0c;攻击者可利用该漏洞执行任意SQL语句&#xff0c;如查询数据、下载数据、写入webshell、执行系统命令以及绕过登录限制等。 fofa&#xff1a;app”用友-GRP-U8” 0x02 POC: /u8qx/license_check.jsp?kj…

Leetcode—2562.找出数组的串联值【简单】

2023每日刷题&#xff08;十四&#xff09; Leetcode—2562.找出数组的串联值 实现代码 long long findTheArrayConcVal(int* nums, int numsSize){int left 0;int right numsSize - 1;long long sum 0;while(left < right) {if(left right) {sum nums[left];break;}…

Linux启动之uboot分析

Linux启动之uboot分析 uboot是什么&#xff1f;一、补充存储器概念1.存储器种类1.norflash - 是非易失性存储器&#xff08;也就是掉电保存&#xff09;2.nandflash - 是非易失性存储器&#xff08;也就是掉电保存&#xff09;3.SRAM - 静态随机访问存储器 - Static Random Acc…

Matlab | 基于二次谱提取地震数据的地震子波

本文通过地震数据二次谱求取地震子波谱&#xff0c;具体方法如下&#xff1a; MATLAB代码实现如下&#xff1a; function w SndSpecExtWavelet(x, M) % 功能&#xff1a;基于二次谱提取输入地震数据data的地震子波wavelet % Extracting Wavelet from Input Seismic Dat…

高等数学啃书汇总重难点(八)向量代数与空间解析几何

持续更新&#xff0c;高数下第一章&#xff0c;整体来说比较简单&#xff0c;但是需要牢记公式&#xff0c;切莫掉以轻心~ 一.向量平行的充要条件 二.向量坐标的线性运算 三.向量的几何性质 四.数量积 五.向量积 六.混合积 七.曲面方程 八.空间曲线方程 九.平面的点法式方程 十…

阿里云的OSS云存储的基本使用

阿里云官网&#xff1a;阿里云-计算&#xff0c;为了无法计算的价值 通过阿里云官网&#xff0c;登录进入用户的界面&#xff0c;在搜索框中输入OSS&#xff0c;然后进入阿里云的对象存储OSS的控制台。&#xff08;未开通的开通即可&#xff09; 创建 Bucket 点击【Bucket 列…

sql在线练习

SQLBolt - 学习 SQL - SQL 简介https://sqlbolt.com/拿走不谢&#xff01;&#xff01;&#xff01; UIUC什么乱七八糟的啊

[UDS] --- RoutineCommunicationControl 0x31

1 0x31功能描述 client端使用RoutineControl服务执行定义的步骤序列并获取任何相关结果。该服务具有很大的灵活性&#xff0c;典型的用法包括擦除内存&#xff0c;复位或学习自适应数据&#xff0c;运行自检&#xff0c;覆盖正常服务器控制策略以及控制服务器值随时间变化等功…

jmeter和postman你选哪个做接口测试?

软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中&#xff0c;有高手&#xff0c;自然也会有小白&#xff0c;但有一点我们无法否认&#xff0c;就是每一个高手都是从小白开始的&#xff0c;所以今天我们就来谈谈一大部分人在做的接口测试&#xff0c;小白变高手…

LabVIEW背景颜色设为和其他程序或图像中一样

LabVIEW背景颜色设为和其他程序或图像中一样 有时候LabVIEW背景色要和其他程序或者图片的颜色保持一致&#xff0c;如果要求不高可以大致设置一下。如果要求较高&#xff0c;那可以按照如下的方式。 先用PS打开标准图像&#xff0c;之后用吸管工具选择图像上中的点&#xff0…

深度学习(生成式模型)——DDIM:Denoising Diffusion Implicit Models

文章目录 前言为什么DDPM的反向过程与前向过程步数绑定DDIM如何减少DDPM反向过程步数DDIM的优化目标DDIM的训练与测试 前言 上一篇博文介绍了DDIM的前身DDPM。DDPM的反向过程与前向过程步数一一对应&#xff0c;例如前向过程有1000步&#xff0c;那么反向过程也需要有1000步&a…