【玩转408数据结构】线性表——线性表的顺序表示(顺序表)
知识回顾
通过前文,我们了解到线性表是具有相同数据类型的有限个数据元素序列;并且,线性表只是一种逻辑结构,其不同存储形式所展现出的也略有不同,那么今天我们来了解一下线性表的顺序存储——顺序表。
顺序表的定义
顺序表指的是将逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。所以顺序表的特点就是其逻辑顺序与其物理顺序相同。
我们不妨将设线性表L存储的起始位置为LOC(A),那么其顺序表L相对应的顺序存储如图所示:(这里sizeof是计算括号内数据元素所占用存储空间的大小)
通过图我们也不难观察出其顺序表的特点。这里每个数据元素的存储位置都与线性表的起始位置相差该数据元素的位序个(n个)数据元素内存大小。所以我们的顺序存储结构是随机存取的存储结构。在接下来高级程序设计语言的实现中,我们决定使用数组来实现该内容(不过需要注意的是,线性表中元素的位序是从1开始的而数组中的元素下标是从0开始的)。
元素类型初始化
静态分配
既然我们了解了顺序表,那么接下来我们就要尝试着去实现顺序表。首先,我们需要思考的是 我们应该怎么定义出顺序表中每个元素的类型呢?这并不是一个困难的问题,由于顺序表的特点,我们这里可以使用一个数组去存放顺序表中元素;不过仅仅使用数组是不行的,因为我们很难去判断我们顺序表存储了多少个元素(顺序表的长度);那么这时,我们就需要一个附加的值(length)去记录我们顺序表的当前长度,由于我们需要两个值同时存在,这里就需要用到我们之前C语言学习时的一个关键字(struct)了。通过我们的思考,我们就可以尝试写出顺序表中的顺序存储类型了。
#define MaxSize 50 typedef struct {int data[MaxSize]; // 定义元素 int length; //表示当前长度
}SqList;
于是我们不难写出上述的代码(需要注意的是,此时data[]为int类型,这里的int可以根据我们存储元素的类型去进行更改)这里我们使用数组去存储顺序表中的元素,使用length去记录当前的长度。
可以,使用该方法(静态分配)去分配时会出现一种问题,由于我们数组的大小和空间是固定的,我们在分配数组时,若数组的空间开的过大会导致其内存的浪费;若空间开的过小,又有可能导致空间占满,进而导致存入新数据时产生溢出、程序崩溃;这也就是我们进行静态分配的缺点。
思考:第三步的Length设为0,可不可以省略? 这当然是不可以的,如果我们没有对Length的值进行初始化,那么这个值在分配的时候将是随机的,这样就会导致长度计算的错误;当然写过一些代码的小伙伴可能会疑惑,我们平时也是没有初始化,他的值一直是0呀,这里主要是由于编译器的原因,我们使用的编译器自动的将其设为0了,但在考试中为了严谨性,还是建议将Length值进行初始化的。
既然静态分配有那么多缺点,那么我们能不能使用一个更好些的办法,去尽可能的避免这些问题呢?答案当然是可以的,这里我们可以采用动态分配。
动态分配
在动态分配中,存储数组的空间实在程序执行的过程中通过动态存储分配语句分配的,一旦该数组的空间占满,就另外开辟出一块更大的存储空间,用来替换掉之前的存储空间,这样可以有效的解决上面的问题。
#define InitSize 50 //顺序表初始长度typedef struct {int *data; // 指向动态分配数组的指针 int MaxSize,length; //分别表示最大容量和当前长度
}SqList;
在进行动态的申请和释放空间时,我们可以利用下面这些关键字:
C —— malloc、free 函数
L.data = (ElemType *) malloc (sizeof(ElemType) *InitSize) ;
C++ —— new、delete 函数
L.data = new ElemType (InitSize) ;
顺序表特点:
- 随机存储,我们可以通过首地址和元素序号在时间复杂度为O(1)内找到指定的元素。
- 其存储密度高,每个节点只需存储数据元素。
- 顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除就需要移动大量的元素。
顺序表的基本操作实现
顺序表的插入
顺序表的插入操作:
ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。
我们的实现思路主要就是,首先,判断输入的第i个位置是否合法;若不合法则插入失败,若合法则将第i个元素及其后面的元素依次向后移动一个位置,然后腾出一个空位置插入新元素e,顺序表的长度增加1,及插入成功。
//插入
bool ListInsert(SqList &L, int i, int e){ //传入顺序表 以及从第i个位置插入一个值e if(i<1 || i>L.length+1) //注:这里的i是表中的第几个元素,并非其数组下标return false ;if(L.length >= MaxSize) //表满 无法插入return false ;//后移for(int j=L.length; j>=i; j--){ //此时j表示的是位数 L.data[j] = L.data[j-1];} /*for(int j=L.length-1; j>=i-1; j--){ j表示的为数组下标 L.data[j+1] = L.data[j];}*/ L.data[i-1] = e ;L.length++;return true ;
}
思考:为什么代码中if语句中用length+1,而for语句中只用length呢?通过对代码的观察我们不难发现,这里if语句和for语句中的元素代表的含义并不相同,if语句中代表的是顺序表元素的位序而for语句中代表的是数组下标。
时间复杂度分析
最好情况:直接在表尾插入元素( i=n+1 ),元素直接后移即可,时间复杂度为O(1)。
最坏情况:在表头插入元素( i=1 ),元素需要后移n次,时间复杂度为O(n)。
平均情况:假设为在第 i 个位置上插入一个结点的概率,则在一个长度为n的线性表中插入一个结点时,需要移动节点的平均次数为:
因此,顺序表插入算法的时间复杂度为O(n)。
顺序表的删除
顺序表的删除操作:
ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。
删除元素我们主要的实现思路就是,我们在删除第i个位置之后,需要将其后面的位置全部向前移动一位,这样就可以完成删除操作了。
//删除
bool ListDelete(SqList &L, int i, int &e){if(i<1 || i>L.length+1)return false ;e = L.data[i-1] ; //第i个元素 在数组的i-1for(int j=i; j<L.length; j++){L.data[j-1] = L.data[j] ;} L.length = L.length-1 ;return true ;}
时间复杂度分析
最好情况:直接在表尾删除元素( i=n+1 ),元素删除即可,时间复杂度为O(1)。
最坏情况:在表头删除元素( i=1 ),元素需要前移n次,时间复杂度为O(n)。
平均情况:假设为在第 i 个位置上删除一个结点的概率,则在一个长度为n的线性表中删除一个结点时,需要移动节点的平均次数为:
因此,顺序表插入算法的时间复杂度为O(n)。
由此可见,插入操作删除操作的时间主要消耗在移动元素上,而移动元素的个数与我们插入或者删除元素的位置有关,不同的插入删除位置所移动的元素个数是不同的。
顺序表的查找
按位查找
GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。
对于按位查找,由于我们的数组下标可以很好的表示出元素的顺序,这里我们就可以直接利用数组下标与元素位序的映射关系去完成返回第i个元素的值操作。
//查找第i个位置的元素值
int GetElem(SqList L, int i) {return L.data[i-1]; //数组下标从0开始
}
时间复杂度分析
由于是直接返回数组值的,所以不需要什么中间的计算,其时间复杂度是稳定的 O(1) 。
按值查找
LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
对于按值查找,我们可以使用循坏,去遍历一遍我们的顺序表,这样就可以找到需要返回的值了;如果遍历一遍之后仍没有发现需要查找的值,那么就返回false,证明查找失败。
//查找
//查找第一个是e的元素 返回其位序
int LocateElem(SqList &L, int e){for(int i=0; i<L.length; i++){ //i为数组下标 if(L.data[i] == e)return i+1 ;}return 0;
}
时间复杂度分析
最好情况:查找的元素在表头,只需要查找一次即可,时间复杂度为O(1)。
最坏情况:查找的元素不存在或者在表尾,需要查找n次,时间复杂度为O(n)。
平均情况:假设为查找元素在第 i 个位置上结点的概率,则在一个长度为n的线性表中查找一个结点时,需要比较节点的平均次数为:
因此,顺序表按值查找算法的时间复杂度为O(n)。
到这里,顺序表的功能也基本完成了,当然对于这些操作,我们动态分配和静态分配的操作代码相差并不大,只是动态分配时需要多出一个增加数组长度的函数,这里在下面的完整代码展示中会体现出来,本文就不做过多描述。
顺序表完整代码
静态分配代码
//2.2 顺序表
#include<bits/stdc++.h>
#define MaxSize 50 using namespace std;typedef struct {int data[MaxSize]; // 定义元素 int length; //表示当前长度
}SqList;int ex = -1 ;//插入
bool ListInsert(SqList &L, int i, int e){ //传入顺序表 以及从第i个位置插入一个值e if(i<1 || i>L.length+1) //注:这里的i是表中的第几个元素,并非其数组下标return false ;if(L.length >= MaxSize) //表满 无法插入return false ;//后移for(int j=L.length; j>=i; j--){ //此时j表示的是位数 L.data[j] = L.data[j-1];} /*for(int j=L.length-1; j>=i-1; j--){ j表示的为数组下标 L.data[j+1] = L.data[j];}*/ L.data[i-1] = e ;L.length++;return true ;
}//删除
bool ListDelete(SqList &L, int i, int &e){if(i<1 || i>L.length+1)return false ;e = L.data[i-1] ; //第i个元素 在数组的i-1for(int j=i; j<L.length; j++){ L.data[j-1] = L.data[j] ;} L.length = L.length-1 ;return true ;} //查找
//查找第一个是e的元素 返回其位序
int LocateElem(SqList &L, int e){for(int i=0; i<L.length; i++){ //i为数组下标 if(L.data[i] == e)return i+1 ;}return 0;
}//查找第i个位置的元素值
int GetElem(SqList L, int i) {return L.data[i-1]; //数组下标从0开始
} int main(){SqList L ;for(int i=0; i<=5; i++){L.data[i] = i+1 ;}L.length = 6 ;for(int i=0; i<=L.length-1; i++) cout << L.data[i] << " " ;cout << endl;ListInsert(L, 3, 3) ;for(int i=0; i<=L.length-1; i++) cout << L.data[i] << " " ;cout << endl;ListDelete(L, 3, ex) ;cout << ex << endl ;for(int i=0; i<=L.length-1; i++) cout << L.data[i] << " " ;cout << endl;cout << GetElem(L, 3) << endl ; cout << LocateElem(L, 3) << endl;return 0;
}
动态分配代码
//2.2 顺序表
#include<bits/stdc++.h>
#define InitSize 50 //顺序表初始长度using namespace std;typedef struct {int *data; // 指向动态分配数组的指针 int MaxSize,length; //分别表示最大容量和当前长度
}SqList;int ex = -1 ;//初始化
void InitList(SqList &L) {L.data = (int *)malloc(sizeof(int));L.length = 0;L.MaxSize = InitSize;
} //动态增长数组
void IncreaseSize(SqList &L, int len) { //len为需要增加长度 int *p = L.data; //p记录之前数组地址 方便后期释放 L.data = (int *)malloc(sizeof(int) * (L.MaxSize+len)) ; //申请一片新的区域 for(int i=0; i<L.length; i++) {L.data[i] = p[i]; //将值复制到新的区域 }L.MaxSize = L.MaxSize+len; //更新最大的容量 free(p); //释放之前动态申请的空间
} //插入
bool ListInsert(SqList &L, int i, int e){ //传入顺序表 以及从第i个位置插入一个值e if(i<1 || i>L.length+1) //注:这里的i是表中的第几个元素,并非其数组下标return false ;if(L.length >= L.MaxSize) //表满 无法插入return false ;//后移for(int j=L.length; j>=i; j--){ //此时j表示的是位数 L.data[j] = L.data[j-1];} /*for(int j=L.length-1; j>=i-1; j--){ j表示的为数组下标 L.data[j+1] = L.data[j];}*/ L.data[i-1] = e ;L.length++;return true ;
}//删除
bool ListDelete(SqList &L, int i, int &e){if(i<1 || i>L.length+1)return false ;e = L.data[i-1] ; //第i个元素 在数组的i-1for(int j=i; j<L.length; j++){ L.data[j-1] = L.data[j] ;} L.length = L.length-1 ;return true ;} //查找
//查找第一个是e的元素 返回其位序
int LocateElem(SqList &L, int e){for(int i=0; i<L.length; i++){ //i为数组下标 if(L.data[i] == e)return i+1 ;}return 0;
}//查找第i个位置的元素值
int GetElem(SqList L, int i) {return L.data[i-1]; //数组下标从0开始
} int main(){SqList L ;InitList(L) ;IncreaseSize(L, 10); for(int i=0; i<=5; i++){L.data[i] = i+1 ;}L.length = 6 ;for(int i=0; i<=L.length-1; i++) cout << L.data[i] << " " ;cout << endl;ListInsert(L, 3, 3) ;for(int i=0; i<=L.length-1; i++) cout << L.data[i] << " " ;cout << endl;ListDelete(L, 3, ex) ;cout << ex << endl ;for(int i=0; i<=L.length-1; i++) cout << L.data[i] << " " ;cout << endl;cout << GetElem(L, 3) << endl ; cout << LocateElem(L, 3) << endl;return 0;
}
两个完整代码的内容大同小异,主要就是在顺序表定义初始化时会产生些许不同,我们主要理解其产生逻辑即可,其代码的运行结果图如下:
由代码可知,我们对其顺序表初始化为(1,2,3,4,5,6)就是我们第一行所展示的数字;之后我们在第三个位置插入3,所以第二行展示的就是插入后的结果;第三行则是输出我们在删除时需要删除的位置,紧接着我们将第三个位置的数字删除,所以第四行显示的是其删除后的结果;最后两行就是输出的为第三个位置和查找值为3的元素在第几个位置并输出。
顺序表的内容到这里也就结束了,我们在下面尽量可以独立的去实现一下代码,这样可以更好的帮助我们理清其内部的逻辑。
相关文章:

【玩转408数据结构】线性表——线性表的顺序表示(顺序表)
知识回顾 通过前文,我们了解到线性表是具有相同数据类型的有限个数据元素序列;并且,线性表只是一种逻辑结构,其不同存储形式所展现出的也略有不同,那么今天我们来了解一下线性表的顺序存储——顺序表。 顺序表的定义 …...

图像处理之《黑盒扰动的可逆噪声流鲁棒水印》论文阅读
一、文章摘要 近年来,基于深度学习的数字水印框架得到了广泛的研究。现有的方法大多采用基于“编码器-噪声层-解码器”的架构,其中嵌入和提取过程分别由编码器和解码器完成。然而,这种框架的一个潜在缺点是编码器和解码器可能不能很好地耦合…...

一个Vivado仿真问题的debug
我最近在看Synopsys的MPHY仿真代码,想以此为参考写个能实现PWM-G1功能的MPHY,并应用于ProFPGA原型验证平台。我从中抽取了一部分代码,用Vivado自带的仿真器进行仿真,然后就遇到了一个莫名其妙的问题,谨以此文作为debug…...
C#阿里云消息列队推送消息
推送消息到队列 IMNS nativeclient new Aliyun.MNS.MNSClient(accessKeyId, accessKeySecret, endpoint, _stsToken);var nativeSend nativeclient.GetNativeTopic("SMQ");nativeSend.PublishMessage("推送消息内容"); 需要引用Aliyun.MNS.dll 下载地址…...

Stable Diffusion 模型下载:majicMIX sombre 麦橘唯美
本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十...

WindowsLinuxmeterepreter渗透命令回顾
最近小编发现在学红队的时候总会忘记一些命令(基础的),导致整天红温,于是今天就来偷个懒记一下(一起回顾一下) 1.Linux 1.查看当前按目录 pwd2.查看文件内容 cat filename.txt3.cd 家族 cd ..|| cd ../…...

KingSCADA实现按钮点击效果
哈喽,你好啊,我是雷工! 在做SCADA项目的时候,按钮是不可缺少的功能,但软件自带的按钮太丑,已经无法满足现如今客户对界面美观度的要求。 这时候就需要UI小姐姐设计美观大气的SCADA界面,但UI设计…...
Python编程-二万字浅谈装饰器原理与装饰器设计模式和函数式编程案例讲解
Python编程-浅析装饰器原理与装饰器设计模式和函数式编程案例讲解 本文制作时基于Python3.11.8与Python3.12.1,存在谬误,请联系修改,希望对你有所帮助 什么是函数式编程 函数式编程(Functional Programming)是一种编程…...

基于Zigbee的智能温室大棚系统(附详细使用教程+完整代码+原理图+完整课设报告)
🎊项目专栏:【Zigbee课程设计系列文章】(附详细使用教程+完整代码+原理图+完整课设报告) 前言 👑由于无线传感器网络(也即是Zigbee)作为🌐物联网工程的一门必修专业课,具有很强的实用性,因此很多院校都开设了zigbee的实训课程;👑同时最近很多使用了我的单片机课…...

【Web】Redis未授权访问漏洞学习笔记
目录 简介 靶机配置 Redis持久化 Redis动态修改配置 webshell 反弹shell Redis写入反弹shell任务 加固方案 简介 Redis(Remote Dictionary Server 远程字典服务器)是一个开源的内存数据库,也被称为数据结构服务器,它支持…...

【JAVA WEB】 css背景属性 圆角矩形的绘制
目录 背景属性设置 圆角矩形 背景属性设置 背景颜色,在style中 background-color:颜色; 背景图片 background-image:url(……) 背景图片的平铺方式 background-repeat: 平铺方式 repeat 平铺(默认)no-repeat 不平铺repeat-x 水平平铺repea…...

Docker-现代化应用部署的利器
一、容器部署的发展 今天我们来说说容器部署。我们知道容器部署的发展大致分三个阶段,下面来介绍一下不同阶段的部署方式的优缺点 物理机部署 优点是可以提供更高的性能、资源控制,也可以提供更好的数据隔离和安全性,因为不同的应用程序运行在…...
「优选算法」:山脉数组的峰顶索引
一、题目 符合下列属性的数组 arr 称为 山脉数组 : arr.length > 3存在 i(0 < i < arr.length - 1)使得: arr[0] < arr[1] < ... arr[i-1] < arr[i] arr[i] > arr[i1] > ... > arr[arr.length - 1] …...
网络安全红队基础建设与介绍
1.ATT&CK相关背景 ATT&CK在各种日常环境中都很有价值。开展任何防御活动时,可以应用ATT&CK防御法,参考攻击者及其行为。ATT&CK不仅对网络防御者提供通用技术库,还为渗透测试和红队提供了基础。提到对抗行为时,这为…...

Java语法学习反射
Java语法学习反射 大纲 基本介绍class的介绍 具体案例 1. 基本介绍 流程图(程序在计算机的阶段) 反射的主要的类 这个提高效率不大 2. class的介绍 对于第三点:首先类只会加载一次,得到的class的对象,也只有一…...

【MySQL】操作库 —— 库的操作 -- 详解
一、增删数据库 1、创建数据库 create database db_name; 本质就是在 /var/lib/mysql 创建一个目录。 说明: 大写的表示关键字。[ ] 是可选项。CHARACTER SET:指定数据库采用的字符集。COLLATE:指定数据库字符集的校验规则。 2、数据库删除…...
Rust安装——Win10
安装步骤 1、下载RUSTUP-INIT.EXE(64-BIT) 2、由于国外源下载依赖太慢,因此建议增加win10环境变量配置国内源,增加RUSTUP_DIST_SERVER、RUSTUP_UPDATE_ROOT环境变量即可 RUSTUP_DIST_SERVER随便选择其中的一个源就行,…...

【教学类-46-07】20240212立体春字1.0
背景需求: 在南浔古镇的非遗文化馆里看到一个新年活动折纸——立体春字, 我记得这个就是一个双三角结构折纸,完全可以用15*15的手工纸给孩子们做一套。 折纸教程 双三角折法 【“鼠”你有才】纸艺教学 剪纸——立体春字(2月23日…...
Python语言例题集(003)
#!/usr/bin/python3 #猜数字 import random secretNumberrandom.randint(1,20) print(‘我想了一个1到20间的整数,你能猜出来吗?’) for guessesTaken in range(1,7): print(‘猜一下!’) guessint(input()) if guess<secretNumber: pr…...

UE5 播放本地MP3、MP4
1.创建一个媒体播放器 2.如创建视频,勾选。 它会多一个媒体纹理给你 3.1 设置音频 在一个actor上添加“媒体音频组件” “音频媒体播放器”赋值给它 3.2播放音频 添加一个音频媒体播放器变量, 赋值 地址使用绝对地址 4.1设置视频 UI上创建一个imag…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...
计算机系统结构复习-名词解释2
1.定向:在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方,那么就可以避免停顿。 2.多级存储层次:由若干个采用不同实现技术的存储…...