【数据结构】顺序表专题详解(带图解析)

news2024/11/25 1:03:37

最好的时光,在路上;最好的生活,在别处。独自上路去看看这个世界,你终将与最好的自己相遇。💓💓💓

目录

•🌙说在前面

🍋知识点一:什么是数据结构

• 🌰1.什么是数据? 

• 🌰2.什么是结构?

• 🌰3.为什么需要数据结构? 

• 🌰4.常见的数据结构分类

🍋知识点二:顺序表

• 🌰1.顺序表的概念及结构

🔥线性表

• 🌰2.顺序表的分类

🔥静态顺序表

🔥动态顺序表

• 🌰3.顺序表的初始化

• 🌰4.判断是否需要申请空间

• 🌰5.顺序表元素的打印

• 🌰6.顺序表头部插入元素

• 🌰7.顺序表尾部插入元素

• 🌰8.顺序表指定位置插入元素

• 🌰9.顺序表头部删除元素

• 🌰10.顺序表尾部删除元素

• 🌰11.顺序表指定位置删除元素

• 🌰12.顺序表的查找

• 🌰13.顺序表的销毁

🍋知识点三:顺序表基本操作

• ✨SumUp结语


•🌙说在前面

亲爱的读者们大家好!💖💖💖,我们又见面了,我们有关C语言部分的语法在上一篇文章就告一段落了,紧接着我将会给大家更新一些有关数据结构的基础知识,帮助大家更好的理解和学习数据结构,希望大家可以有所收获。

今天这篇文章所讲解的是有关顺序表的知识,包括了顺序表的增加、删除、查找、插入元素等一系列操作,感谢大家支持!

在数据结构的学习中,画图是必不可少的,所以在数据结构的部分我将会尽可能用图画的方式让大家直观的感受到它的原理,其实只要思路到位,代码部分就是信手拈来~

初阶数据结构的部分将会用C语言进行实现~

💘💘💘数据结构的学习离不开刷题,这里🌷给大家推荐两个很好的学习刷题网站——力扣、牛客网,各种题目应有尽有,大家可以在上面挑选合适难度的题进行训练

👇👇👇

各位友友们!🎉🎉🎉点击这里进入牛客网🎉🎉🎉

👇👇👇

🎉🎉🎉这里是力扣🎉🎉🎉

 

  博主主页传送门:愿天垂怜的博客

🍋知识点一:什么是数据结构

数据结构是由"数据"和"结构"两词组合而来。

• 🌰1.什么是数据? 

常见的数值1、2、3、4......、教务系统里保存的用户信息(姓名、性别、年龄、学历等等)、网页上肉眼可以看到的相关信息(文字、图片、视频等等),这些都是数据。

• 🌰2.什么是结构?

当我们想要使用大量同一类型的数据时,通过定义大量的独立的变量,可读性非常差,而且非常不方便,这时我们可以借助数组这样的数据结构将大量的数据组织在一起。结构也可以理解为组织数据的方式。

生活中也有这样的例子:

🔥想要找到草原上名叫"小白"的样很难

🔥但是从羊圈里找到1号羊就很简单,羊圈这样的结构有效将羊群组织起来

概念:数据结构是计算机在内存中存储和组织数据的方式。

数据结构关注如何以最有效的方式组织和存储数据,以便在计算机程序中进行操作和处理。

    

• 🌰3.为什么需要数据结构? 

如图所示,不借助排队的方式来管理客户,会导致客户就餐感受差、等餐时间长、餐厅营业混乱等情况。同理,程序中如果不对数据进行管理,可能会导致数据丢失、操作困难、野指针等情况。用过数据结构,能够有效将数据组织和管理在一起。按照我们的方式任意方式对数据进行增、删、查、改等操作。

• 🌰4.常见的数据结构分类

数据结构一般根据组织形式,分为:线性数据结构和非线性数据结构。

线性的数据结构有:数组、链表、栈和队列等。

非线性的数据结构有:树、散列表、堆、图。

 最基础的数据结构 - 数组

那我们已经有了数组,为什么还要学习其他的数据结构呢?

假定数组有10个空间,已经使用了5个,向数组中插入数据步骤:

求数组的长度,求数组的有效数据个数,向下标为数据有效个数的位置插入数据(注意:这里是否要判断数组是否满了,满了还能继续插入吗?)......

假设数据量非常庞大,频繁的获取数组有效个数会影响程序执行效率。

结论:最基础的数据结构能够提供的操作已经不能完全满足复杂算法实现。

🍋知识点二:顺序表

• 🌰1.顺序表的概念及结构

🔥线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串......

线性表在逻辑上是线性结构,也就是说是连续的一条直线。但是在物理结构上并不一定是连续的,

比如:

蔬菜分为绿叶类、瓜类、菌菇类。线性表指的是具有部分相同特性的一类数据结构的集合。

那如何理解逻辑结构和物理结构的连续?

 以链表为例,上面每个节点分别存储了数据和指向下一个节点的地址,不难发现这些节点的地址是各不相同的,并不是像数组一样在内存中连续存放;但是由于每个节点存储了指向下一个节点的地址,所以通过一个节点我们可以找到下一个节点,由此每个节点之间都产生了联系,从上面的图中我们发现,这种联系是连续的,所以我们称链表在物理结构上不连续,在逻辑结构上连续。

• 🌰2.顺序表的分类

顺序表底层就是数组,它是对数组的封装,在数组的基础上增加了增删查改的方法。

🔥静态顺序表

概念:使用定长数组存储元素

#define N 100
struct SeqList
{
	int arr[N];//定长数组
	int size;//顺序表当前有效的数据个数
};

定义结构体SeqList,在结构体中有一数组,其长度N必须是确定的,在数组中有效的数据个数为size,这样定义出的顺序表称为静态顺序表

🔥动态顺序表
struct SeqList
{
	int* arr;
	int size;//有效的数据个数
	int capacity;//空间大小
};

定义结构体SeqList,在结构体中有一数组, 其长度capacity是不确定的,在数组中有效的数据个数为size,这样定义出的顺序表称为动态顺序表

📌那是静态顺序表更好还是动态顺序表更好呢?

答案是,动态顺序表

原因有二:

🎉对于静态顺序表,由于其长度是确定的,如果数组大小给小了,空间不够用,如果给大了,造成空间浪费。

🎉对于动态顺序表,由于其长度不确定,我们可以按需申请所需要的大小,对其进行增容和减容。

所以在顺序表中我们一般都使用动态顺序表。

注意:

在数据结构中,由于频繁利用结构体类型,在定义结构体时通常用以下形式同时对结构体类型进行重定义:

typedef struct SeqList
{
	int* arr;
	int size;
	int capacity;
}SL;

同时,由于顺序表的元素不一定只是int型,也可以是别的类型,如果后期要修改数据类型,一个一个改比较麻烦,通常用typedef重定义如下: 

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* arr;
	int size;
	int capacity;
}SL;

• 🌰3.顺序表的初始化

实现顺序表的增删查改功能,我们使用的都是动态顺序表。

C语言实现初始化顺序表的代码如下:

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

初始化需要将arr置为NULL,同时需要将数组的有效长度和空间大小设置为0。

📌为什么这里用传址调用而不用传值调用?

形参只是实参的一份临时拷贝,对形参的修改并不会影响实参。若传递结构体变量,是不会改变结构体变量本身的值的,如果我们需要在函数中改变一个变量的值,则需要传递这个值的地址,所以需要传址调用。

• 🌰4.判断是否需要申请空间

在初始化动态顺序表后我们就需要对其开开辟空间,或在程序判断出顺序表内存空间已满后要进行顺序表空间的扩容处理,所以最好可以封装一个函数,达到既可以判断最大空间与当前成员的关系(即判断满没满),又能在将顺序表扩容。

void SLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		SLDataType NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* temp = (SLDataType*)realloc(ps->arr, NewCapacity * sizeof(SLDataType));
		if (temp == NULL)
		{
			perror("realloc");
			exit(1);
		}
		else
		{
			ps->arr = temp;
			ps->capacity = NewCapacity;
		}
	}
}

注意我们一般情况下扩容为原来大小的两倍左右,但是我们也要考虑到空间大小为0的情况,比如我们刚初始化完的顺序表,那此时的空间大小我们直接设置成4个字节,所以我们创建了一个SLDataType类型的Newcapacity,表示扩容后的大小,如果原空间大小为0就为4,否则就为原来大小的两倍,然后用realloc函数进行增容,增容失败就显示错误信息并且退出,增容成功就将增容后的新地址赋值给顺序表中的数组,同时空间的大小也更新为新的NewCapacity。

• 🌰5.顺序表元素的打印

C语言实现顺序表元素的打印:

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

打印元素就非常简单了,一个for循环直接搞定。由于在打印元素的过程中没有对原结构体进行修改,所以可以不需要传地址,直接传值就好。

• 🌰6.顺序表头部插入元素

例如一顺序表含有2、3、4、5、6,希望在2的前面,也就是最开始的地方添加一个数据1,就是在顺序表的头部插入元素

void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	ps->size++;
}

在对顺序表进行插入元素,都应该先检查ps是否为空,以及ps->arr是否需要扩容,然后使用for循环,让顺序表中的每个元素都向后移动一位,然后再将第一个元素置为我们所想要插入的数据x,就可以了。

• 🌰7.顺序表尾部插入元素

例如一顺序表含有1、2、3、4、5,希望在5的后面,也就是最后一个元素的后面添加一个数据6,就是在顺序表的尾部插入元素

void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = x;
}

尾插相对于头插就更简单了,直接在末尾添加数据即可,同时有效的数据个数也增加1就可以了。

• 🌰8.顺序表指定位置插入元素

例如一顺序表含有1、2、4、5、6,希望在2和4的中间添加一个数据3,也就是在下标为2(pos=2)的位置插入数据3,这就是指定位置插入元素

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

此时我们再保证ps不为NULL的同时也需要保证元素下标pos的值是大于等于0且小于等于ps->size的,然后将pos之后的元素全部向后移动一位,在向pos位置插入要添加的数据SLDataType x即可。

• 🌰9.顺序表头部删除元素

例如一顺序表含有0、1、2、3、4、5,希望删除最开头的元素0,也就是顺序表的头部,这就是头部删除元素

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);
	for (int i = 0; i < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

头部删除元素,只需要将出了第一个元素之外的所有元素向前移动一位就行了,这样直接就能将它覆盖。

• 🌰10.顺序表尾部删除元素

例如一顺序表含有1、2、3、4、7,希望删除最末尾的元素7,也就是顺序表的尾部,这就是尾部删除元素

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

尾删就很简单了,直接有效长度-1就可以了,毕竟有效长度之前的才算是有效的顺序表的元素。

 

• 🌰11.顺序表指定位置删除元素

例如一顺序表含有1、2、3、3、5,希望删除2和第二个3中间的数据3,也就是删除下标为(pos=2)的位置的元素,这就是指定位置删除元素

void SLErase(SL* ps ,int pos) 
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	for (int i = pos; i < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

想要删除pos位置的元素,需要将角标pos到ps->size-1的元素全体向前移动一位,将pos位置的元素覆盖即可。 

• 🌰12.顺序表的查找

例如有一顺序表1、2、3、4、5、... n-1、n,想在这个顺序表中查找数据x,这就是顺序表的查找

int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
			return i;
	}
	return -1;
}

用for循环遍历数组,在查找的过程中,如果顺序表中确实有数据x,则返回x的下标i,如果没有查找到这个数据,则返回-1,也就是说返回的是-1说明顺序表中没有该元素。

• 🌰13.顺序表的销毁

在利用顺序表成功实现相对应的功能后,需要将顺序表进行销毁

void SLDestroy(SL* ps)
{
	if (ps->arr)
	{
		free(ps->arr);
	}
    ps->arr=NULL;
	ps->size = ps->capacity = 0;
}

由于ps->arr申请的是堆上的动态内存,使用完之后我们需要释放这部分内存,释放后还最好将ps->arr置为NULL,以防造成野指针,同时将有效数据个数和空间大小设置为0。

 

🍋知识点三:顺序表基本操作

 顺序表的基本操作就是实现对顺序表元素的增、删、查、改,关于如何实现已经在前面都进行了讲解,也给出了代码,现在希望大家掌握之后通过下面给出的SeqList.h头文件,在SeqList.c文件中分别实现这些功能,并在test.c的main函数中测试:

#pragma once
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* arr;
	int size; //
	int capacity;
}SL;

//初始化和销毁
void SLInit(SL * ps);
void SLDestroy(SL * ps);

//顺序表的打印
void SLPrint(SL * ps);

//判断是否需要申请空间
void SLCheckCapacity(SL * ps);

 //头部插入删除 / 尾部插入删除
void SLPushBack(SL * ps, SLDataType x);
void SLPopBack(SL * ps);
void SLPushFront(SL * ps, SLDataType x);
void SLPopFront(SL * ps);

//指定位置插入/删除数据
void SLInsert(SL * ps, int pos, SLDataType x);
void SLErase(SL * ps, int pos);

//查找元素
int SLFind(SL * ps, SLDataType x);

• ✨SumUp结语

数据结构的学习一定要多画图,多理解,多思考,切忌直接抄写代码,就认为自己已经会了,一定到自己动手,才能明白自己哪个地方有问题。

如果大家觉得有帮助,麻烦大家点点赞,如果有错误的地方也欢迎大家指出~

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

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

相关文章

python爬虫学习------scrapy第三部分(第三十一天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

数字社交风潮:解析Facebook的影响力

随着互联网的普及和科技的发展&#xff0c;数字社交媒体已经成为现代社会不可或缺的一部分。在众多的社交媒体平台中&#xff0c;Facebook作为其中的佼佼者&#xff0c;影响着数以亿计的用户。本文将深入解析Facebook的影响力&#xff0c;探讨其在数字社交风潮中的地位和作用。…

Spring Security基础教程:从入门到实战

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c…

2024.1.1 IntelliJ IDEA 使用记录

2024.1.1 IntelliJ IDEA 使用记录 下载设置文件编码maven 配置 插件可以中文语言包安装lombok 插件Smart Tomcat ( 根据需要安装)Smart Tomcat 配置 项目导入java 设置maven 配置 项目运行SpringBoot 项目运行tomcat 运行 (根据需要)相关依赖添加运行配置 下载 IntelliJ IDEA …

5000A信号发生器使用方法

背景 gnss工作需要使用的5000A&#xff0c;所以做成文档&#xff0c;用于其他员工学习。 下载星历数据 https://cddis.nasa.gov/archive/gnss/data/daily/2024/brdc/ 修改daily中的年份&#xff0c;就可以获取相关截至时间的星历数据 brcd数据格式 第一行记录了卫星的PRN号&a…

软考中级-软件设计师(十)网络与信息安全基础知识

一、网络概述 1.1计算机网络的概念 计算机网络的发展&#xff1a;具有通信功能的单机系统->具有通信功能的多机系统->以共享资源为目的的计算机网络->以局域网及因特网为支撑环境的分布式计算机系统 计算机网络的功能&#xff1a;数据通信、资源共享、负载均衡、高…

根据最近拒包项目总结,详细讲解Google最新政策(上)

关于占比最多的移动垃圾软件拒审问题 移动垃圾软件(Mobile Unwanted Software)特征表现1> 具有欺骗性,承诺其无法实现的价值主张。2> 诱骗用户进行安装,或搭载在用户安装的其他程序上。3> 不向用户告知其所有主要功能和重要功能。4> 以非预期方式影响用户的系统…

Linux磁盘IO、网络IO、零拷贝详解

一、什么是I/O&#xff1f; 在计算机操作系统中&#xff0c;所谓的I/O就是输入&#xff08;input&#xff09;和输出&#xff08;output&#xff09;,也可以理解为读&#xff08;read&#xff09;和写&#xff08;write&#xff09;,针对不同的对象&#xff0c;I/O模式可以划分…

探索5个独特AI工具:它们是否值得独立存在?

在这个“地下AI”系列的最新一集中&#xff0c;我们深入挖掘了一些鲜为人知的AI工具。这些工具并非出自OpenAI、微软或谷歌等科技巨头之手&#xff0c;而是独立创造者和小型团队的智慧结晶。我们的目标是发现利用最新AI技术的独特工具。但这次有个新玩法&#xff1a;我们玩一个…

新能源汽车动力电池热管理-液冷方案应用原理与应用前景简介

前言 动力电池是新能源汽车的核心部件之一&#xff0c;其性能和寿命直接影响着车辆的续航里程和使用成本。液冷方案作为一种常见的动力电池温控解决方案&#xff0c;被广泛应用于新能源汽车领域。本文将详细介绍液冷方案的原理、发展方向以及市场前景。 一、液冷方案的原理 …

记录一个练手的js逆向password

很明显 请求加密了password 全局搜索 有个加密函数(搜不到的可以搜临近的其他的关键字 或者url参数) 搜索的时候一定要仔细分析 我就没有仔细分析 我搞了好久 又是xhr又是hook的(还没hook到) 我当时也是疏忽了 我寻思这个也不是js文件 直到后来 我怎么也找不到 我就猜想 不…

01.本地工作目录、暂存区、本地仓库三者的工作关系

1.持续集成 1.持续集成CI 让产品可以快速迭代&#xff0c;同时还能保持高质量。 简化工作 2.持续交付 交付 3.持续部署 部署 4.持续集成实现的思路 gitjenkins 5.版本控制系统 1.版本控制系统概述2.Git基本概述3.Git基本命令 2.本地工作目录、暂存区、本地仓库三者的工作关系…

python abs函数怎么用

abs()函数是Python的数字函数&#xff0c;用以返回数字的绝对值。 语法 以下是 abs() 方法的语法&#xff1a; abs( x ) 参数 x -- 数值表达式&#xff0c;可以是整数&#xff0c;浮点数&#xff0c;复数。 返回值 函数返回 x&#xff08;数字&#xff09;的绝对值&#x…

Scanner中next()、nextInt()、nextLine()、hasNext()、hasNextInt()的使用方法及注意事项

目录 1、next()、nextInt()、nextLine()的使用方法及区分 2、循环时如何使用hasNext方法 3、用hasNextInt()作为判断下一个输入是否为数字需要配合next()方法使用 1、next()、nextInt()、nextLine()的使用方法及区分 三者简单定义 next()&#xff1a;此方法遇见第一个有效字符…

通过 Java 操作 redis -- 基本通用命令

目录 使用 String 类型的 get 和 set 方法 使用通用命令 exists &#xff0c;del 使用通用命令 keys 使用通用命令 expire,ttl 使用通用命令 type 要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务器&#xff0c;推荐看通过 Java 操作 redis -- 连接 redis 关…

裸辞、裁员、老板跑路、被迫失业,未来是「超级个体」的时代

本期我们邀请的程序员是张立强&#xff0c;裸辞、裁员、老板跑路、被迫失业&#xff0c;管理层利益争夺&#xff0c;职业转型&#xff0c;工作五年&#xff0c;攒出了十年经验。程序员如何寻找自己的第二曲线&#xff0c;不妨听听立强的看法。 裸辞失业 大家好&#xff0c;我…

redis 使用记录

redis 使用记录 下载运行配置文件启动 参考 下载 github: Redis for Windows 或者从百度网盘下载 Redis version 3.2.100 链接: https://pan.baidu.com/s/1kxNOuZFunvVhVy1cfQzCDA?pwdpibh 运行 双击运行 运行效果 如果出错&#xff1a;查看是否项目路径是否包含中文 配…

计算机视觉——OpenCV Otsu阈值法原理及实现

算法简介 Otsu阈值法&#xff0c;也被称为大津算法&#xff0c;是一种在图像处理中广泛使用的自动阈值分割技术。这种方法由日本学者大津展之于1979年提出&#xff0c;旨在根据图像的灰度直方图来自动选择最佳全局阈值。Otsu阈值法的核心思想是最小化类内方差或最大化类间方差…

京东淘宝1688商品采集商品数据抓取API

item_get-获得淘宝商品详情 item_search 关键字搜索商品 公共参数 请求地址: taobao/item_search 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&a…

Azure AKS日志查询KQL表达式

背景需求 Azure&#xff08;Global&#xff09; AKS集群中&#xff0c;需要查询部署服务的历史日志&#xff0c;例如&#xff1a;我部署了服务A&#xff0c;但服务A的上一个版本Pod已经被杀掉由于版本的更新迭代&#xff0c;而我在命令行中只能看到当前版本的pod日志&#xff…