【数据结构】顺序表及其实现

news2025/6/23 16:16:27

目录

1.线性表

2.顺序表 

2.1顺序表的概念及结构

2.2顺序表的实现 


1.线性表

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

线性表在逻辑上是线性结构,但在物理结构上不一定是连续的;线性表在物理上存储时,通常以数组和链式结构的形式存储

2.顺序表 

2.1顺序表的概念及结构

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

顺序表一般可分为:

1️⃣静态顺序表:使用定长数组存储元素

2️⃣动态顺序表:使用动态开辟的数组存储

实现顺序表时,我们采用的是动态的顺序表 

2.2顺序表的实现 

顺序表初始化

可以看到初始化函数的形参是一个指针类型,因为我们要对顺序表结构中的结构成员进行增删查改,所以需要传址调用

//顺序表初始化
void SLInit(SL* psl)
{
	assert(psl);//避免空指针的访问
	psl->a = NULL;
	psl->capacity = psl->size = 0;
}

顺序表销毁 

当创建一个顺序表并增删查改后,其结构如下图

销毁顺序表时,不仅要释放psl指向的结构,其结构成员array指向的数组占用的内存也要释放

📖Note:

结构本身不占用内存空间,只有使用结构创建结构变量时才占用内存,psl是我们创建的结构变量

//顺序表销毁
void SLDestroy(SL* psl)
{
    assert(psl);
    //如果顺序表不为空,进行内存释放
	if (psl->a)
	{
		free(psl);
		psl->a = NULL;
		psl->capacity = psl->size = 0;
	}
}

顺序表打印 

采用for循环打印有效数据即可

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

首先,插入数据时无论尾插,头插还是指定位置插入,都需要检查顺序表的容量,因为创建的是动态顺序表,所以检查容量发现顺序表已满时,需要为顺序表重新开辟空间

检查容量函数

为了内存的有效使用,第一次动态开辟尽量不要开辟太大的空间,这里我们第一次开辟4块空间,第二次动态开辟时,扩容2倍

为什么扩容2倍?

解:扩容时,扩多了存在空间浪费,扩少了会导致频繁扩容,降低效率,所以扩2倍比较合适

newcapacity是空间容量的大小,每块空间存储的是一个SLDataType类型的数据,所以使用realloc函数扩容时,需要的空间应该为newcapacity*sizeof(SLDataType)

尾插数据:

因为顺序表采用数组存储,所以可以直接通过下标访问顺序表中的元素

顺序表的有效数据为size个,尾插数据即在a[size]位置插入

每次成功插入数据后,记录顺序表有效数据元素个数的变量size要+1

//检查容量
void SLCheckCapacity(SL* psl)
{
	if (psl->size == psl->capacity)
	{
		//第一次开辟4块空间,否则开辟原来的两倍
		int newcapacity = (psl->capacity == 0) ? 4 : (psl->capacity) * 2;
		SLDataType* tmp = (SLDataType*)realloc(psl->a, newcapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		psl->a = tmp;
		psl->capacity = newcapacity;
	}
}
//尾插
void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);
	//检查容量
	SLCheckCapacity(psl);
	//尾插
	psl->a[psl->size] = x;
	psl->size++;
}

首先,删除数据时无论尾删,头删还是指定位置删除,都需要检查顺序表的容量,如果顺序表为空则不能进行删除操作

尾删 

如下代码所示,有两种方法检查顺序表是否为空,较常用的是检查2

删除元素只需要有效数据元素个数减一即可,这样访问不到a[size]位置的元素即已经删除

//尾删
void SLPopBack(SL* psl)
{
	assert(psl);
	//检查:如果顺序表为空则不能进行删除
	//检查1:
	/*if (psl->size == 0)
	{
		return;
	}
	psl->size--;*/
	//检查2:顺序表为空则报错
	assert(psl->size > 0);
    psl->size--;
}

头插

头插需要先检查顺序表的容量

对于数组结构,头插数据需要挪动数据,较高效的挪动方法是从后向前依次挪动,如下图:

插入数据后,记录顺序表有效数据元素个数的变量size要+1

//头插
void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	//检查容量
	SLCheckCapacity(psl);
	//头插需要挪动数据,从后向前依次挪动
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[0] = x;
	psl->size++;
}

头删

头删需要先检查顺序表的容量,顺序表为空则不能进行删除

头删步骤:

1️⃣删除a[0]位置的元素

2️⃣后续元素从前向后依次向前挪动

实际上步骤1和2可以合并为,从第二个元素开始依次覆盖前一个元素

删除数据后,记录顺序表有效数据元素个数的变量size要-1

//头删
void SLPopFront(SL* psl)
{
	assert(psl);
	//检查:如果顺序表为空则不能进行删除
	assert(psl->size > 0);
	//从前向后依次向前挪动,覆盖前一个数据即可
	int begin = 0;
	while (begin<psl->size -1)
	{
		psl->a[begin] = psl->a[begin + 1];
		begin++;
	}
	psl->size--;
}

查找

使用for循环遍历数组,找到符合要求的数据则返回下标,遍历未找到则返回-1

//查找
int SeqListFind(SL* psl, SLDataType x)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	//遍历后未找到
	return -1;
}

在指定位置处插入元素

首先要对指定的位置的值进行检查,如果超出数组下标则报错

指定位置插入元素需要检查容量,调用检查容量函数即可

在pos处插入数据需要挪动数据,从pos开始向后的数据需要依次向后挪动一位,如下图:

 插入数据后,记录顺序表有效数据元素个数的变量size要+1

//在pos处插入一个元素
void SeqListInsert(SL* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos <= psl->size);
	//检查容量
	SLCheckCapacity(psl);
	//pos之后的所有元素向后移动,从后向前依次移动
	//pos是size_t类型,防止整型提升导致不能进行头插,end也应定义为size_t类型
	size_t end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end - 1];
		end--;
	}
	psl->a[pos] = x;
	psl->size++;
}

删除指定位置处元素

删除指定位置pos处的元素,从pos+1位置开始,依次向后,每个元素覆盖前一个元素即可

 删除数据后,记录顺序表有效数据元素个数的变量size要-1

//删除pos处的元素
void SeqListErase(SL* psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);
	size_t begin = pos;
	//从pos+1开始,从前向后依次覆盖前一个数据
	while (begin < psl->size - 1)
	{
		psl->a[begin] = psl->a[begin + 1];
		begin++;
	}
	psl->size--;
}

修改指定位置处的元素

因为数组可以通过下标访问,所以当已知位置时,可以通过下标直接修改pos处的元素

//修改pos处的数据
void SLModify(SL* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos < psl->size);
	psl->a[pos] = x;
}

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

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

相关文章

Parallels Desktop 18 18.3.1激活攻略

如果说虚拟机领域有一位王者&#xff0c;非Parallels不能领袖群伦&#xff0c;毕竟大厂背书&#xff0c;功能满格&#xff0c;美中不足之处就是价格略高&#xff0c;但这也并非是Parallels的错&#xff0c;因为市场上没有任何一款虚拟机产品在产品力层面能和Parallels抗衡&…

使用Typora+PicGo+阿里云搭建图床

1.为什么要使用图床 不知道大家有没有遇到过这样的问题&#xff1f; 在使用Typora的时候&#xff0c;我们传到typora上面的图片&#xff0c;在转到其他地方时&#xff0c;总是加载不出来&#xff0c;造成图片丢失现象或者是在将markdown笔记上传到博客时&#xff0c;总是需要一…

华为OD机试真题 JavaScript 实现【静态代码扫描服务】【2023Q1 100分】

一、题目描述 静态扫描快速识别源代码的缺陷&#xff0c;静态扫描的结果以扫描报告作为输出&#xff1a; 文件扫描的成本和文件大小相关&#xff0c;如果文件大小为N&#xff0c;则扫描成本为N个金币&#xff1b;扫描报告的缓存成本和文件大小无关&#xff0c;每缓存一个报告…

(二)安装 Kafka

文章目录 1.选择操作系统2.配置 Java 环境3.安装 ZooKeeper4.安装 broker&#xff08;1&#xff09;安装 broker&#xff08;2&#xff09;验证是否安装正确 5.配置 broker&#xff08;1&#xff09;常规配置&#xff08;2&#xff09;主题的默认配置 6.配置 Kafka 集群&#x…

Netty之协议设计

目录 为什么需要协议 redis协议示例 http协议举例 自定义协议 要素 编解码器 测试 为什么需要协议 TCP/IP 中消息传输基于流的方式&#xff0c;没有边界。 协议的目的就是划定消息的边界&#xff0c;制定通信双方要共同遵守的通信规则 例如&#xff1a;在网络上传输 …

c++11 标准模板(STL)(std::ios_base)(三)

定义于头文件 <ios> class ios_base; 类 ios_base 是作为所有 I/O 流类的基类工作的多用途类。它维护数种数据&#xff1a; 1) 状态信息&#xff1a;流状态标志&#xff1b; 2) 控制信息&#xff1a;控制输入和输出序列格式化和感染的本地环境的标志&#xff1b; 3)…

(一)Flask简介和快速使用

关于Python三大Web框架浅谈一嘴&#xff1a; Django、Flask和Tornado三个框架都是Python Web应用的开发框架&#xff0c;虽然它们都能够开发Web应用&#xff0c;但在使用方式、适用领域和处理方式上还是有很多不同的。 Django Django是一个高层次&#xff08;大而全&#xff0…

Flutter自定义系列之折线波动图,心率图,价格走势图

随着前两篇文章的学习&#xff0c;我今天继续给大家演示下简单的自定义之折线波动图&#xff0c;心率图&#xff0c;价格走势图。 这里&#xff0c;我们创建一个自定义的StatefulWidget&#xff0c;用于显示动态的价格线。 我们将使用CustomPaint和CustomPainter来绘制价格线…

chatgpt赋能python:Python中如何截断字符串

Python中如何截断字符串 Python是一种简单易学、高效的编程语言&#xff0c;旨在让开发人员更快、更方便地完成任务。然而&#xff0c;在实际开发过程中&#xff0c;我们常常需要对字符串进行截断操作。那么&#xff0c;Python中怎么截断字符串呢&#xff1f;接下来就让我们来…

如何最大限度地利用ChatGPT、Bard和其他聊天机器人

作者&#xff1a;Hayden Field 译者&#xff1a;明明如月 当下&#xff0c;随着生成式人工智能的发展&#xff0c;面向消费者的聊天机器人能够处理不同领域的需求&#xff0c;并提供相应的帮助和建议&#xff0c;如制定商业战略、设计数学学习指南、提供薪资谈判建议&#xff…

chatgpt赋能python:Python字符串截断-解决方式及实现方法

Python字符串截断-解决方式及实现方法 在Python编程中&#xff0c;处理字符串是一个非常常见的任务。其中&#xff0c;字符串截断也是在许多场景下必不可少的功能之一。Python不仅提供了许多内置函数来处理字符串&#xff0c;而且还有许多方法来截断字符串。 什么是字符串截断…

chatgpt赋能python:Python怎么截图速度快?

Python怎么截图速度快&#xff1f; 在现在这个数字时代&#xff0c;我们所有人都需要进行屏幕截图。无论是用于记录重要笔记&#xff0c;制作教程&#xff0c;或是用于软件质量控制&#xff0c;高速、高质量、高效的屏幕截图工具都非常必要。 在Python编程领域中&#xff0c;…

S3C2440A的ARM工作模式以及寄存器种类

文章目录 前言一、ARM的工作模式二、寄存器的种类&#xff08;注意特殊寄存器的使用&#xff09;总结 前言 本期和大家主要分享的是ARM工作模式以及寄存器种类&#xff0c;不同系列的ARM的工作模式以及寄存器的种类大同小异&#xff0c;所以针对于S3C2440A&#xff0c;一定得通…

【题目解析】第六届字节后端青训营结营小测试全解析

前言 &#x1f44f; Hi! 我是 Yumuing&#xff0c;一个技术的敲钟人 &#x1f468;‍&#x1f4bb; 每天分享技术文章&#xff0c;永远做技术的朝拜者 &#x1f4da; 欢迎关注我的博客&#xff1a;Yumuing’s blog 由于官方答案没有出来&#xff0c;所以&#xff0c;这部分都是…

03.填充中断向量表IDT,使用中断

填充中断描述符表IDT&#xff0c;使用中断 通过初始化中断控制芯片&#xff0c;编码中断函数&#xff0c;实现BIOS中断 操作系统的中断是一种异步事件&#xff0c;用于通知 CPU 某个事件已经发生&#xff0c;例如硬件设备完成数据传输、发生错误或用户发起的系统调用。当操作系…

栈和队列(栈的应用)[二]

文章目录 栈的应用一、栈在系统中的应用简化路径(leetcode. 71) 二、扩号匹配问题有效的括号(leetcode. 20) 三、字符串去重删除字符串中的所有相邻重复项(leetcode. 1047) 四、逆波兰表达式问题逆波兰表达式求值(leetcode. 150) 总结 栈的应用 递归的实现是栈&#xff1a;每一…

使用腾讯手游助手作为开发测试模拟器的方案---以及部分问题的解决方案-1

目录 前言: 一.目录结构 二.注册表研究 1.HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Tencent\MobileGamePC 2.HKEY_CURRENT_USER\Software\Tencent\MobileGamePC 三.模拟器快捷启动 1.快捷启动命令: 2.启动命令如何放入桌面: 3.adb端口,目前测试均可以使用: 前言: 此…

PyTorch深度学习实战(3)——使用PyTorch构建神经网络

PyTorch深度学习实战&#xff08;3&#xff09;——使用PyTorch构建神经网络 0. 前言1. PyTorch 构建神经网络初体验1.1 使用 PyTorch 构建神经网络1.2 神经网络数据加载1.3 模型测试1.4 获取中间层的值 2. 使用 Sequential 类构建神经网络3. PyTorch 模型的保存和加载3.1 模型…

【框架源码】Spring源码解析之Bean生命周期流程

观看本文前&#xff0c;我们先思考一个问题&#xff0c;什么是Spring的bean的生命周期&#xff1f;这也是我们在面试的时候&#xff0c;面试官常问的一个问题。 在没有Spring之前&#xff0c;我们创建对象的时候&#xff0c;采用new的方式&#xff0c;当对象不在被使用的时候&…

【网络】UDP/TCP网络程序

目录 UDP网络程序 简单通信版本(UDP) 准备工作&#xff08;接口学习、分析&#xff09; 整体代码&#xff08;Server.hpp/Server.cpp/Client.hpp/Client.cpp&#xff09; 添加“婴儿版”业务逻辑 英译汉翻译 my_shell 聊天室 linux和windows通信 TCP网络程序 简单通…