探索数据结构:顺序表的实现与应用

 🔑🔑博客主页:阿客不是客

🍓🍓系列专栏:渐入佳境之数据结构与算法

欢迎来到泊舟小课堂

😘博客制作不易欢迎各位👍点赞+⭐收藏+➕关注

一、什么是顺序表

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

简单来讲就是用一段物理地址连续的存储单元依次存储数据元素的线性结构,它与数组非常类似,但是相比于数组顺序表有一个非常明显的优点——可以动态内存增长空间大小

我们常用的数组有很多的缺点:

而使用顺序表的动态开辟的数组存储就方便很多

二、顺序表的定义

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空 间开多了浪费,开少了不够用。

所以现实中基本都是使用动态顺序表,根据需要动态的分配空间 大小,所以下面我们实现动态顺序表。

什么是接口函数?

 接口函数是在数据进行操作时进行调用的函数,通过调用数据结构的接口帮助你完成一系列操作

void SLInit(SL* p1);//初始化
void SLDeseroy(SL* p1);//销毁void SLPrint(SL* p1);void SLCheckCapacity(SL* p1);//扩容void SLpushback(SL* p1, SLDataType x);//尾插
void SLpushfront(SL* p1, SLDataType x);//头插
void SLpopback(SL* p1);//尾删
void SLpopfront(SL* p1);//头删//任意位置增删
void SLinsert(SL* p1, int pos, SLDataType x);
void SLerase(SL* p1, int pos);//找到返回下标
//没找到返回-1
int SLfind(SL* p1, int pos, SLDataType x);

2.1 创建项目

为了养成模块化好习惯,我们尽量把代码分开来写。首先打开vs2022,在解决方案资源管理器中的 "头文件" 文件夹中创建 SeqList.h 用来存放头文件。在 "源文件" 文件夹中创建 顺序表.cpp 用来实现函数,Test.c 用来测试我们的顺序表:

为了方便后续修改数据类型,我们可以使用 typedef 定义一个新的数据类型,这里我们把它取名为 SLDataType(可以任意)。

我们为了让定义的结构体使用时更方便,我们同样可以使用 typedef 将其定义为 SL

(此时 SL = struct SeqList,后续在使用时可以更加方便)。

2.2 定义顺序表 

定义顺序表我们首先需要一个动态内存开辟的空间,当前数据的个数(size),以及方便扩容的容量大小

typedef int SLDataType;
typedef struct Seqlist//顺序表
{SLDataType* a;     //动态开辟的数组int size;	   //数据个数int capacity;   //容量大小
}SL;

三、顺序表接口功能的实现

3.1 初始化顺序表

首先引入我们自己创建的头文件 #include "SeqList.h" ,我们就可以开始动手实现顺序表初始化函数了。

首先通过 psl 指向 array,将数组为空。因为是初始化,所以将有效数据个数和数组时即能存数据的空间容量一并置为0。

#include "SeqList.h"
void SLInit(SL* p1)
{p1->a = NULL;p1->size = 0;p1->capacity = 0;
}

3.2 销毁顺序表

因为是动态开辟的,所以如果空间不用我们就需要销毁掉。如果不销毁会存在内存泄漏的风险,所以与之对应的我们写一个销毁的接口函数。

void SLDeseroy(SL* p1)
{assert(p1);  //断言if (p1->a != NULL){free(p1->a);//释放动态开辟的空间p1->a = NULL;//置空p1->size = 0;//数据个数为0p1->capacity = 0;//容量大小为0}
}

3.3 扩容

后续我们会插入元素,如果空间不够,则使用realloc函数扩容。

newCapacitv是扩容后的内存空间,tmp是指向这个·新的空间的指针。如果空间为0,则扩容可以放置4个元素的空间,如果空间已满,则在原来的空间基础上,在增加1倍,这样其实依然是有浪费的,后面我们会解决这个问题。

void SLCheckCapacity(SL* p1)
{assert(p1);if (p1->size == p1->capacity)//检查容量是否满了,满了就翻倍{int newCapacity = p1->capacity == 0 ? 4 : p1->capacity * 2;//三目运算符,为空设置为4SLDataType* tmp = (SLDataType*)realloc(p1->a, sizeof(SLDataType) * newCapacity);if (tmp == NULL)//防止原先地址被覆盖{perror("realloc fail");return;}p1->a = tmp;p1->capacity = newCapacity;//更新容量}
}

3.4 打印数据

实现函数后,我们如果想要打印到屏幕上,需要实现打印函数,这样我们每次实现一个功能,测试时,只需调用这个函数就可以了

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

3.5 尾插数据

尾插数据,顾名思义就是在顺序表末尾插入数据,在插入数据之前我们应该检查顺序表是否为满。

使用上面的扩容函数

void SLpushback(SL* p1, SLDataType x)
{assert(p1);  //断言SLCheckCapacity(p1);//检查容量是否满了p1->a[p1->size] = x;p1->size++;//有效个数+1
}

3.6 头插数据 

顺序表要求数据是连续存储的,且必须是从头开始存储。所以,对于顺序表而言如果要实现头插,就需要把数据往后挪动。不能从前往后挪,如果从前往后挪就挪就会把后面的数据覆盖掉。 

 思路:首先创建一个 end 变量用来指向要移动的数据,因为指向的是数据的下标,所以是 size 要减 1 。随后进入 while 循环,如果 end >= 0 说明还没有移动完,就会进入循环。循环体内利用下标,进行向后移动操作,移动结束后再 end-- ,进行下一个数据的向后移动。挪动数据成功后,就可以插入了。此时顺序表第一个位置就被腾出来了,就可以在下标0位置插入欲插入的数据 x 了。最后记得 size++ 。

void SLpushfront(SL* p1, SLDataType x)
{assert(p1);  //断言SLCheckCapacity(p1);//检查顺序表容量是否已满int end = p1->size - 1;//从后往前依次向后挪动数据while (end >= 0){p1->a[end + 1] = p1->a[end];end--;}p1->a[0] = x;p1->size++;
}

3.7 尾删数据

同理,尾删数据就是删除顺序表中最后一个元素,大部分人所想也许是把最后一个元素置为0或者-1,这是可行的,但如果最后一个数就是0呢?

其实我们这里只需要元素数量减去一个就好了,即size--,这样我们就无法访问最后一个元素了,它便是无效的数据。注意:删除之前要保证顺序表中有数据

void SLpopback(SL* p1)
{assert(p1);//p1->a[p1->size-1] = NULL;//会被覆盖,可以不需要/*温柔的检查if (p1->size == 0){printf("数据已经全部删除了\n");return;}*///暴力的检查assert(p1->size > 0);p1->size--;
}

这里有可能会出现如果内存中一个元素都没有了,size有可能减到-1的位置上,这便是越界了,但size是我们用来统计元素数量的,不可能小于0的,所以这里我们需要断言一下。

3.8 头删数据

思路和头插类似,依次向前挪动,然后数量-1即size--

 

  如果头删多次调用,如何内存中已经没有元素了,size依然在减。所以这里依然会出现越界,所以需要断言。

void SLpopfront(SL* p1)
{assert(p1->size > 0);int begin = 1;while (begin < p1->size){p1->a[begin - 1] = p1->a[begin];//从前往后依次向前挪动begin++;}p1->size--;
}

3.9 任意位置插入数据

与头插类似,从指定位置之后的全部向后挪动

代码思路:

首先添加 pos 位置的限定条件,限定 pos >= 0 并且 pos <= psl->size 从而保证 pos 合法。然后,因为是插入所以免不了要检查增容,直接调用之前写好的检查增容的函数即可。检查完后就可以开始移动了,和头插差不多,我们创建一个变量 end 记录最后一个的下标(psl->size-1),并通过它来指向要移动的数据。最后进入 while 循环,以 end >= pos 作为条件。移动完后,x 的位置就腾出来了,再把 x 插入进去,最后再 size++,就完成了。

 

void SLinsert(SL* p1, int pos, SLDataType x)
{assert(p1);assert(pos >= 0 && pos <= p1->size);SLCheckCapacity(p1);int end = p1->size - 1;//挪动数据while (end >= pos){p1->a[end + 1] = p1->a[end];//覆盖end--;}p1->a[pos] = x;p1->size++;
}

3.10 任意位置删除数据

删除指定位置的数据,我们仍然要限制 pos 的位置。限制条件部分和 SeqListInsert 不同的是,因为 psl->size 这个位置没有效数据,所以删除的位置不能是 psl->size

 

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

 任意位置删除的函数可以替代头删和尾删

3.11 查找指定数据

根据输入参数,在顺序表中查找指定的值并返回其下标,若未找到则返回-1。

int SLfind(SL* p1, int pos, SLDataType x)
{assert(p1);for (int i = 0;i < p1->size;i++){if (p1->a[i] == x){return i;}}return -1;
}

四、完整代码

4.1 SLDataType.h

#pragma once
#include<stdio.h>
#include<string>
#include<stdlib.h>
#include<assert.h>typedef int SLDataType;
//单词SL,Date,type
typedef struct Seqlist//顺序表
{SLDataType* a;     //动态开辟的数组int size;	   //数据个数int capacity;   //容量大小
}SL;void SLInit(SL* p1);//初始化
void SLDeseroy(SL* p1);//销毁void SLPrint(SL* p1);void SLCheckCapacity(SL* p1);//扩容void SLpushback(SL* p1, SLDataType x);//尾插
void SLpushfront(SL* p1, SLDataType x);//头插
void SLpopback(SL* p1);//尾删
void SLpopfront(SL* p1);//头删//任意位置增删
void SLinsert(SL* p1, int pos, SLDataType x);
void SLerase(SL* p1, int pos);//找到返回下标
//没找到返回-1
int SLfind(SL* p1, int pos, SLDataType x);

4.2 SLDataType.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SLDataType.h"
void SLInit(SL* p1)
{p1->a = NULL;p1->size = 0;p1->capacity = 0;
}void SLDeseroy(SL* p1)
{assert(p1);  //断言if (p1->a != NULL){free(p1->a);//释放动态开辟的空间p1->a = NULL;//置空p1->size = 0;//数据个数为0p1->capacity = 0;//容量大小为0}
}void SLPrint(SL* p1)
{for (int i = 0;i < p1->size;i++){printf("%d ", p1->a[i]);}printf("\n");
}void SLCheckCapacity(SL* p1)
{assert(p1);if (p1->size == p1->capacity)//检查容量是否满了,满了就翻倍{int newCapacity = p1->capacity == 0 ? 4 : p1->capacity * 2;//三目运算符,为空设置为4SLDataType* tmp = (SLDataType*)realloc(p1->a, sizeof(SLDataType) * newCapacity);if (tmp == NULL)//防止原先地址被覆盖{perror("realloc fail");return;}p1->a = tmp;p1->capacity = newCapacity;//更新容量}
}void SLpushback(SL* p1, SLDataType x)
{assert(p1);  //断言SLCheckCapacity(p1);//检查容量是否满了p1->a[p1->size] = x;p1->size++;//有效个数+1
}void SLpushfront(SL* p1, SLDataType x)
{assert(p1);  //断言SLCheckCapacity(p1);//检查顺序表容量是否已满int end = p1->size - 1;//从后往前依次向后挪动数据while (end >= 0){p1->a[end + 1] = p1->a[end];end--;}p1->a[0] = x;p1->size++;
}void SLpopback(SL* p1)
{assert(p1);//p1->a[p1->size-1] = NULL;//会被覆盖,可以不需要/*温柔的检查if (p1->size == 0){printf("数据已经全部删除了\n");return;}*///暴力的检查assert(p1->size > 0);p1->size--;
}void SLpopfront(SL* p1)
{assert(p1->size > 0);int begin = 1;while (begin < p1->size){p1->a[begin - 1] = p1->a[begin];//从前往后依次向前挪动begin++;}p1->size--;
}void SLinsert(SL* p1, int pos, SLDataType x)
{assert(p1);assert(pos >= 0 && pos <= p1->size);SLCheckCapacity(p1);int end = p1->size - 1;//挪动数据while (end >= pos){p1->a[end + 1] = p1->a[end];//覆盖end--;}p1->a[pos] = x;p1->size++;
}void SLerase(SL* p1, int pos)
{assert(p1->size > 0);assert(pos >= 0 && pos <   p1->size);int begin = pos + 1;while (begin < p1->size){p1->a[begin - 1] = p1->a[begin];begin++;}p1->size--;
}int SLfind(SL* p1, int pos, SLDataType x)
{assert(p1);for (int i = 0;i < p1->size;i++){if (p1->a[i] == x){return i;}}return -1;
}

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

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

相关文章

5分钟教你APP变现,让商业浪潮为你助力!

在这个数字时代&#xff0c;几乎每个人都有一个或多个应用程序&#xff08;APP&#xff09;的想法&#xff0c;它们可能是为了解决特定问题&#xff0c;提供娱乐或简化日常任务。然而&#xff0c;许多开发者面临的最大挑战之一是如何将这些创意转化为盈利的商业模式。本文将探讨…

从简单到复杂,红酒配餐的层次感与变化

红酒配餐是一种艺术&#xff0c;通过不同层次的搭配&#xff0c;可以呈现出丰富的味觉变化&#xff0c;使每一口都充满惊喜。云仓酒庄雷盛红酒以其卓着的品质和与众不同的口感&#xff0c;为红酒配餐提供了无限可能。从简单到复杂&#xff0c;红酒配餐的层次感与变化如下&#…

windows系统万能激活码可长期使用参考

windows万能激活码参考 windows10万能激活码可长时间使用&#xff0c;无需半年重新激活的哦。激活操作步骤 windows10万能激活码可长时间使用&#xff0c;无需半年重新激活的哦。 激活操作步骤 选择 ‘此电脑’ 中的 ‘属性’&#xff1b; 选择 ‘更改产品密钥’ 选项&#x…

有类似注册表编辑器的vb6源码吗?vba注册表编辑器

第一步是要实现注册表功能的读写&#xff0c;所有数据类型&#xff0c;枚举列出所有子项 第二步&#xff0c;树形控件之类显示&#xff0c;可视化修改&#xff0c;查看 第三步&#xff0c;导入导出注册表 第四步&#xff0c;注册表监控&#xff0c;检测哪些注册表项是新建、删除…

Gopher的Rust第一课:第一个Rust程序

经过上一章[1]的学习&#xff0c;我想现在你已经成功安装好一个Rust开发环境了&#xff0c;是时候撸起袖子开始写Rust代码了&#xff01; 程序员这个历史并不算悠久的行当&#xff0c;却有着一个历史悠久的传统&#xff0c;那就是每种编程语言都将一个名为“hello, world”的示…

MT3048 区间按位或

思路&#xff1a; 使用ST表。ST表求区间最大值改为按位或即可。 ST模板可参考MT3024 maxmin 代码&#xff1a; 1.暴力6/10 #include <bits/stdc.h> using namespace std; const int N 2e5 10; const int M 5e5 10; int n, m; int num[N]; int main() {cin >&…

从 ASCII 到 UTF-8 - Unicode 码的诞生与实现

前言&#xff1a;最近我在整理过往笔记时&#xff0c;发现涉及到了 UTF-8、Unicode 的相关内容&#xff0c;相信大家中的很多人和之前的我一样&#xff0c;在过去的很长一段时间里&#xff0c;并没有搞清楚什么是 Unicode、什么是 UTF-8&#xff0c;于是就有了这篇文章&#xf…

腾讯面试:如何提升Kafka吞吐量?

面试题大全&#xff1a;www.javacn.site Kafka 是一个分布式流处理平台和消息系统&#xff0c;用于构建实时数据管道和流应用。它最初由 LinkedIn 开发&#xff0c;后来成为 Apache 软件基金会的顶级项目。 Kafka 特点是高吞吐量、分布式架构、支持持久化、集群水平扩展和消费组…

RK平台ADB不识别问题排查

简介 ADB是Android系统的调试工具&#xff0c;一般用USB线连接开发板和PC&#xff0c;可以抓取开发板的调试日志&#xff0c;执行shell指令&#xff0c;传输文件等功能。为了调试方便&#xff0c;RK平台的Linux系统也默认支持ADB&#xff0c;其源码是从Android移植过来的。 本…

「浏览器」跨站请求伪造CSRF攻击的原理以及防范措施

前言 HTTP 是一个无状态的协议&#xff0c;比如需要账号密码登录的网站这个场景&#xff0c;为了避免每次都需要重复输入&#xff0c;有一种方案就是Cookie&#xff0c;具体使用不做赘述&#xff0c;但是这样带来了一些安全问题。跨站请求伪造&#xff08;CSRF&#xff09;攻击…

Java日志体系概述

一. 日志体系分类 1.1 功能分类 1.2 jar包分类 二. 以log4j2为例 2.1 slf4j-api的初始化动态绑定过程 一. 日志体系分类 1.1 功能分类 门面类 slf4j&#xff1a;Simple Logging Facade for Java&#xff0c;为java提供的简单日志Facade具体实现类 logbacklog4j1log4j2jul&…

Windows下切换不同版本的CUDA

在环境变量处将需要使用的CUDA版本的如图所框选的环境变量移到其他版本环境变量的前方即可 PS&#xff1a;改环境变量后重启命令行再查看版本~

Three.js是基于原生WebGL封装的三维引擎

Three.js: 基于原生WebGL封装的三维引擎 引言 随着互联网技术的发展&#xff0c;Web前端技术不断进步&#xff0c;用户对于网页交互体验的要求也越来越高。艾斯视觉前端开发&#xff1a;三维技术作为提升用户体验的重要手段之一&#xff0c;正在逐渐成为前端开发中的热门技术…

pdf只要其中一页 pdf只要第一页怎么办 pdf只要前几页怎么弄

在现代办公环境中&#xff0c;PDF文件已经成为我们日常工作中不可或缺的一部分。然而&#xff0c;有时我们可能只需要PDF文件中的某一页&#xff0c;而不是整个文件。这时&#xff0c;我们该如何操作才能只获取所需的那一页呢&#xff1f;本文将详细操作方法&#xff0c;帮助大…

Spark中的累加器与广播变量及blockmanager图解

一、累加器 1、累加器的引入 案例&#xff1a;没读取一条文件中的数据&#xff0c;count1&#xff0c;并打印在Drive端&#xff08;控制台&#xff09; import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.rdd.RDDobject Demo20Accumulator {def ma…

应用弹窗优先级

背景 由于活动业务越来越多,积累的弹窗越来越多和杂乱,出现如下弹窗交互问题: 弹窗无限重叠,影响操作 弹出顺序无优先级,重要弹窗被隐藏 原因相信大家都一样,产品是一次次迭代的,也可能是不同人开发的,两个毫不相关的业务,弹窗时机也没有任何关联,重不重叠我怎么控制…

有效运营企业内部社区的板块有哪些?

随着企业内部沟通和协作的重要性日益凸显&#xff0c;建立一个高效运营的企业内部社区成为越来越多企业的首要任务。针对不同的需求和目标&#xff0c;将企业内部社区分为多个板块&#xff0c;可以更好地促进员工之间的沟通、协作和共享知识。下面介绍如何从分多个板块创建的角…

SQL注入攻击是什么?如何预防?

一、SQL注入攻击是什么&#xff1f; SQL注入攻击是一种利用Web应用程序中的安全漏洞&#xff0c;将恶意的SQL代码插入到数据库查询中的攻击方式。攻击者通过在Web应用程序的输入字段中插入恶意的SQL代码&#xff0c;然后在后台的数据库服务器上解析执行这些代码&#xff0c;从而…

定个小目标之每天刷LeetCode热题(3)

这是一道简单题&#xff0c;我这里就只讲两种解法 第一种是数组加双指针&#xff0c;先遍历链表将值存到数组里&#xff0c;然后分别从数组两端进行一一比较判断是否满足回文&#xff0c;代码实现 class Solution {public boolean isPalindrome(ListNode head) {List<Inte…

给想玩AIGC的小白:教你从0搭一个图文视频生成网站(附插件源码)

Stable Diffusion的发布是AI图像生成发展过程中的一个里程碑&#xff0c;相当于给大众提供了一个可用的高性能模型&#xff0c;让「AI 文本图片生成」变成普通人也能玩转的技术。最近一些网友将网上的真人图片不断喂给模型&#xff0c;让其自主学习&#xff0c;训练出来的效果已…