《动态顺序表》的实现

news2025/2/23 17:42:55

目录

前言:

认识线性表:

关于顺序表

实现动态顺序表:

顺序表的动态存储定义:

初始化顺序表:

顺序表的销毁:

尾插: 

判断是否需要扩容:

总代码:

 头插:

 打印顺序表:

尾删: 

头删: 

指定下标插入数据:

 指定下标删除:

查找元素下标:

总结:


前言:

从本文开始,我们就正式进入到了《数据结构》这门课程的入门学习,既然是入门,那我们就从较为基础和简单的部分“顺序表“开始我们的学习旅程。本文主要以模拟实现顺序表的增删查改等功能。该部分与我们之前所讲的《动态内存实现通讯录》较为相似,建议在学习此内容前,可以对《通讯录》这一部分进行复习和浏览:运用动态内存实现通讯录(增删查改+排序)-CSDN博客

认识线性表:

线性表是一种数据结构,是由n个具有相同数据类型的数据元素(节点)构成的有限序列。其中,n为线性表的长度,也称为表长。线性表中只有一个开始节点,也只有一个终止节点。线性表的两种常见实现方式是顺序存储和链式存储。

顺序存储是将线性表中的元素顺序存储在一块连续的存储空间(数组)中,通过元素在数组中的下标来访问元素。

链式存储是通过指针将线性表中的元素存储在离散的存储空间中。每个节点都包含元素信息和一个指向下一个节点的指针,通过遍历指针来访问元素。

线性表常见的操作有:插入、删除、查找、遍历等。其中查找操作主要包括按位置查找和按元素值查找。

线性表的应用非常广泛,常见的应用场景包括数组、链表、栈、队列、字符串等。

 

关于顺序表

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

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

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

静态顺序表只适用于确定知道需要存多少数据的场景。

静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小。

下面我们实现动态顺序表

实现动态顺序表:

顺序表的动态存储定义:

typedef int SLDataType;

typedef struct Seqlist
{
	SLDataType* a;//指向动态开辟的数组
	size_t sz;//有效数据个数
	size_t capacity;// 容量空间大小
}SL;

初始化顺序表:

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

以上这一部分在我们的通讯录所讲的内容是一致的,如有不懂的还是可以再回顾回顾动态内存实现通讯录的部分,在这里我不做过多的赘述。

顺序表的销毁:

void SLDestory(SL* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->capacity = 0;
	psl->sz = 0;
}

尾插: 

对于尾插,顾名思义就是从尾巴插入元素,即在顺序表的数组中,在最后一个位置插入。

但这里我们要考虑一种情况,那就是那我们的预先开辟的空间满了的时候,要先进行扩容操作,再进行尾插,因此便有了以下的函数实现:

判断是否需要扩容:

void SLCheckCapacity(SL* psl)
{
	assert(psl);
	if (psl->sz == psl->capacity)
	{
		size_t newcapacity = (psl->capacity == 0) ? 4 : 2 * (psl->capacity);

		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType)*newcapacity);
		if (tmp == NULL)
		{
			perror("SLCheckCapacity -> realloc");
			return;
		}

		psl->a = tmp;
		psl->capacity = newcapacity;
	}
}

对于以下代码,我们使用了三目操作符,如果我们一开始的空间capacity为0,那么我就给它一个初始空间4。

size_t newcapacity = (psl->capacity == 0) ? 4 : 2 * (psl->capacity);

 如果不是并且满了,我们就进行2倍扩容。

这里还要注意的是,realloc是可以对空指针进行扩容的,但是效果相当于malloc开辟空间!!

 当扩容操作结束后,就该进行真正的尾插操作。

总代码:

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);
	SLCheckCapacity(psl);

	psl->a[psl->sz] = x;
	psl->sz++;
}

 头插:

对于头插我们则需要将a[0]之后的所有元素从后开始,依次往后挪一格,直到腾出a[0]的位置。

所以我们在此可以创建一个指针指向,顺序表最后一个元素,再往前遍历。

顺序表的最后一个元素的下标,熟练之后想都不用想,就是sz - 1!

记住结论!

数组最后一个元素的下标 = 当前元素个数 -1

所以就有了end = psl->sz - 1; 

所以总代码为:

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	SLCheckCapacity(psl);

	size_t end = psl->sz - 1;
	while (end)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[0] = x;
	psl->sz++;
}

但是对于该算法,如果我们要插入n个数,那么该算法的时间复杂度就是O(N^2) 

因此不推荐头插。

而尾插的时间复杂度就直接O(1),所以我们尽量选择尾插而非头插! 

 打印顺序表:

该函数的实现比较简单,在这里我不做过多的赘述。

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

尾删: 

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

对于该函数的实现,我们的重点就是要注意这个当前数据个数,即SZ的值。

如果我们当前本就没有值,即psl->sz == 0时。

若再进行减减操作,则会在下一次打印的时候出现漏打的现象。

因此我们在这里直接使用assert这种暴力的提醒方式,告诉你函数哪一行出现了错误:

这就是assert的作用!

头删: 

 对于头删,我们可以与刚刚所讲的头插进行联系,其实就是将第一个元素删除,即a[0]上面的元素被a[1]覆盖,而a[1]被a[2]覆盖,如此以来循环往复,就可以先创建个下标,然后循环遍历。

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

这里我们和尾删保持一致,先对当前数据个数进行assert断言判断。

指定下标插入数据:

既然是插入数据,那么我们也肯定是要创建指针来挪动数据,再将数据赋值到那个空位置。

代码实现如下:

void SLPushInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	assert(pos >= 0 && pos <= psl->sz);
	int end = psl->sz - 1;
	while (end >= pos)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[pos] = x;
	psl->sz++;
}

 指定下标删除:

 该函数实现于上述函数同理:

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

查找元素下标:

该函数可用于以上的指定元素删除和插入下标。

具体代码在之前也有讲解,在这里我也不做过多的赘述。

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

总结:

 以上我们实现了《顺序表》的初步,说实话该顺序表的实现与之前我们的对通讯录实现动态内存开辟的方式的思维较为类似,下来可以适当的复习通讯录的内容。

记住多练习多画图才可以明白!

“坐而言不如起而行”

Action speak louder than words!

以下是我的Gitee,里面有完整的代码,包括对菜单的实现!

Data structures amd algorithms: 关于数据结构和算法的代码 - Gitee.com

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

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

相关文章

树莓派结合Nginx,轻松搭建内网穿透服务实现远程访问内部站点

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

C++17中std::any的使用

类sdk:any提供类型安全的容器来存储任何类型的单个值。通俗地说&#xff0c;std::any是一个容器&#xff0c;可以在其中存储任何值(或用户数据)&#xff0c;而无需担心类型安全。void*的功能有限&#xff0c;仅存储指针类型&#xff0c;被视为不安全模式。std::any可以被视为vo…

C语言实现输出一行文字中最长单词

完整代码&#xff1a; // 输出一行文字中最长单词 #include<stdio.h>#define N 20int main(){char str[N];printf("请输入一行文字\n");gets(str);//最长字符串的长度int max0;//str数组的下标int i0;//记录最长字符串在数组中的位置int flag0;//记录每个单词…

【sosp2023论文分享】Ditto:一个弹性自适应分离式内存缓存系统

Ditto: An Elastic and Adaptive Memory-Disaggregated Caching System 摘要 Ditto首先提出了一个以客户端为中心的缓存框架,以在DM的计算池中高效地执行各种缓存算法,只依赖于远程内存访问。然后,Ditto采用了一种分布式自适应缓存方案,该方案基于多个缓存算法的实时性能自…

链式二叉树的基本操作和相关OJ题训练(建议收藏!!!)

&#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;数据结构&C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &#x1f339;&#x1f339;&#x1f339;关注我带你学习编程知识 链式二叉树基本操作 二叉树节点设置二叉…

FMC驱动LCD

硬件简介 主控&#xff1a;STM32H750 LCD屏幕为16位并口屏幕 CubeMX配置 chip select: 选择起始地址块号&#xff0c;ADDR[27:26] Memory type: 内存类型&#xff0c;选择LCD Interface LCD Register Select: 根据选择计算映射地址, FSNC_A[25] Data: 数据宽度 NOR/PSRAM ti…

RISC-V与RISC Zero zkVM的关系

1. 引言 本文基本结构为&#xff1a; 编程语言背景介绍RISC-V虚拟机作为zkVM电路为何选择RISC-V&#xff1f; 2. 编程语言背景介绍 高级编程语言不专门针对某个架构&#xff0c;其便于人类编写。高级编程语言代码&#xff0c;经编译器编译后&#xff0c;会生成针对专门某架…

Azure 机器学习 - 设置 AutoML 训练时序预测模型

目录 一、环境准备二、训练和验证数据三、配置试验支持的模型配置设置特征化步骤自定义特征化 四、可选配置频率和目标数据聚合启用深度学习目标滚动窗口聚合短时序处理非稳定时序检测和处理 五、运行试验六、用最佳模型进行预测用滚动预测评估模型精度预测未来 七、大规模预测…

网络基础扫盲--TCP/UDP

博客内容&#xff1a;TCP/UDP 文章目录 一、TCP与UDP二、tcp可靠性如何保证&#xff1f;1、三次握手2、TCP报头3、TCP四次挥手 一、TCP与UDP tcp与udp都是传输层的协议&#xff0c;用于数据的传输。 对于tcp而言数据的传输会比较可靠&#xff0c;主要依赖于tcp面向链接的&…

openGauss学习笔记-114 openGauss 数据库管理-设置安全策略-设置帐号有效期

文章目录 openGauss学习笔记-114 openGauss 数据库管理-设置安全策略-设置帐号有效期114.1 注意事项114.2 操作步骤 openGauss学习笔记-114 openGauss 数据库管理-设置安全策略-设置帐号有效期 114.1 注意事项 创建新用户时&#xff0c;需要限制用户的操作期限&#xff08;有…

Prompt 设计与大语言模型微调,没有比这篇更详细的了吧!

本文主要介绍了Prompt设计、大语言模型SFT和LLM在手机天猫AI导购助理项目应用。 ChatGPT基本原理 “会说话的AI”&#xff0c;“智能体” 简单概括成以下几个步骤&#xff1a; 预处理文本&#xff1a;ChatGPT的输入文本需要进行预处理。 输入编码&#xff1a;ChatGPT将经过预…

如何使用Python和matplotlib绘制机器人运动偏差路径图——实用教程与代码解析

前言 科研论文中需要绘制一个精美的机器人运动路径图&#xff08;其中包含了机器人的期望运动路径和实际运动路径&#xff09;。我的期望路径是是一个方形&#xff0c;用Python代码是这样表示的&#xff1a; n_list [n1,n2,n3,n4] e_list [e1,e2,e3,e4] #n_list和e_list的各…

C++笔记之动态数组的申请和手动实现一个简单的vector

C笔记之动态数组的申请和手动实现一个简单的vector code review! 文章目录 C笔记之动态数组的申请和手动实现一个简单的vector1.C语言中动态数组的申请与使用1.动态数组的申请使用new和delete使用std::vector 1.std::vector的底层实现2.手动实现一个简单的vector:使用一个指向…

项目实战:展示第一页数据

1、在FruitDao接口中添加查询第一页数据和查询总记录条数 package com.csdn.fruit.dao; import com.csdn.fruit.pojo.Fruit; import java.util.List; //dao &#xff1a;Data Access Object 数据访问对象 //接口设计 public interface FruitDao {void addFruit(Fruit fruit);vo…

文件复制加密、文件落地加密、文件移动加密如何设置?

文件加密在保护信息安全方面具有重要作用。合格、好用的文件加密软件可以帮助企业保护商业机密、更遵守法律法规、提高企业核心竞争力、防止数据泄密等。 一般的加密都是对文件本身加密&#xff0c;比如加密某个WORD /PPT之类的&#xff0c;很少有能够加密某个文件夹的。今天就…

单链表基本操作的实现,初始化,头插,尾插,判空,获取个数,查找,删除,获取前置和后置位,清空,销毁

目录 一.单链表的设计 二.单链表的实现 三.单链表的总结 一.单链表的设计 1.单链表的结构定义: typedef struct Node{int data;//数据域struct Node* next;//后继指针}Node,*List; 2.单链表的设计示意图: 3.注意,单链表的最后一个节点的next域为NULL; 4.为什么要有一个头…

唯一ID如何生成,介绍一下目前技术领域最常使用的几种方法

纵使十面大山&#xff0c;又如何&#xff0c;无妨… 概述 唯一ID&#xff08;Unique Identifier&#xff09;是在计算机科学和信息技术领域中用于标识某个实体或数据的唯一标识符。生成唯一ID的方法可以根据具体需求和应用场景的不同而有所不同。以下是一些目前技术领域中常用…

2023-mac rz sz 安装

之前安装过一次&#xff0c;没问题&#xff0c;这次按照之前教程装了就不管上传下载都会卡住&#xff1b; step1: brew install lrzsz step2&#xff1a;在/usr/local/bin 路径下配置两个sh,之前从网上找到的直接用都不对&#xff0c;下面这个是调试过的正式可用的 iterm2…

【C语言进阶】之动态内存管理

【C语言进阶】之动态内存管理 1.为什么我们需要动态内存管理2.动态内存管理的函数介绍2.1malloc函数和free函数2.1.1malloc函数2.1.2 free函数 2.2calloc函数2.3realloc函数 3.动态内存管理中经常出现的一些问题总结。3.1 越界访问3.2 对空指针进行解引用操作3.3 对同一片空间进…

安全防御——二、ENSP防火墙实验学习

安全防御 一、防火墙接口以及模式配置1、untrust区域2、trust区域3、DMZ区域4、接口对演示 二、防火墙的策略1、定义与原理2、防火墙策略配置2.1 安全策略工作流程2.2 查询和创建会话 3、实验策略配置3.1 trust-to-untrust3.2 trust-to-dmz3.3 untrust-to-dmz 三、防火墙的区域…