线性表——(2)线性表的顺序存储及其运算的实现

 

归纳编程学习的感悟,
记录奋斗路上的点滴,
希望能帮到一样刻苦的你!
如有不足欢迎指正!
共同学习交流!
🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言​📝
     看到美好,感受美好,屏蔽阴霾!

一起加油!

目录

 

一、顺序表:

 二、顺序表基本运算的实现:

💦顺序表的初始化:

💦插入运算 :

☘️顺序表的数据元素的插入算法:

💦删除运算: 

☘️顺序表的数据元素的删除算法:

💦按值查找 :

☘️顺序表的数据元素查找算法:

三、顺序表的其他运算举例:

💦例1:

☘️顺序表的划分算法: 

 💦例2:

☘️顺序表的合并算法:

 💦例3:

☘️两个顺序表的比较算法: 

四、总结: 

五、共勉:


一、顺序表:

        线性表的顺序存储是指在内存中用地址连续的一块存储空间顺序存放线性表中的各数据元素,用这种存储形式存储的线性表称为顺序表。因为内存中的地址空间是线性的,所以用物理位置关系上的相邻性实现数据元素之间的逻辑相邻关系既简单又自然。线性表的顺序存储示意图如图 A所示,设 a 的存储地址为 Loc(a),每个数据元素占 d 个存储单元,则第 i个数据元素的地址为:

Loc(a^{_{i}})=Loc(a^{_{1}})+(i-1)*d       1<=i<=n

图A  线性表的顺序存储示意图

         这就是说,只要知道顺序表首地址每个数据元素所占存储单元的个数就可求出第 i 个数据元素的地址,这也是顺序表具有按数据元素的序号随机存取的特点
        在程序设计语言中,一维数组在内存中占用的存储空间就是一组连续的存储区域,因此,用一维数组来表示顺序表的数据存储区域是再合适不过的了。考虑到线性表有插入、删除等运算,即表长是可变的,因此,数组的容量要设计得足够大,设用 data[MAXSIZE]来表示,其中 MAXSIZE是一个根据实际问题定义的足够大的整数,线性表中的数据元素从 data[0]开始依次顺序存放,但当前线性表中的实际数据元素个数可能未达到 MAXSIZE 个,因此需用变量 last 记录当前线性表中最后一个数据元素在数组中的位置,即 last 具有指针的作用,始终指向线性表中最后一个数据元素,因此,表空时 last =-1。这种存储思想的具体描述可以是多样的,如可以是:

datatype data[MAXSIZE];int last;

        这样表示的顺序表如图 A 所示,表长为 last+1,数据元素分别存放在 data[0]到 data[last]中这样使用简单方便,但有时不便于管理。

        从结构性上考虑,通常将 data 和 last 封装成一个结构,作为顺序表的数据类型: 

typedef struct{datatype data[MAXSIZE];int last;
}SeqList; 

         定义一个顺序表:

SeqList L

        这样表示的线性表如图(a) 所示。表长 = L.last+1,线性表中的数据元素 a1至 an分别存放在 L.data[0]至 L.data[L.last]中.其实有时定义一个指向 SeqList 类型的指针更为方便: 

SeqList *L

        L是一个指针变量,线性表的存储空间通过 L=malloc(sizeof(SeqList)运算来获得。L 中存放的是顺序表的地址,这样表示的线性表如图 (b) 所示。表长表示为(*L).last+1 或 L->last+1,线性表的存储区域为 L->data,线性表中数据元素的存储空间为: 

L->data[0] ~ L->data[L->last]

 

 二、顺序表基本运算的实现:

        根据以上线性表顺序存储结构定义,确定了其存储方式之后,就可以讨论其基本运算的实现方法(即实现算法)了,同时还要对算法进行初步的分析。

💦顺序表的初始化:

        顺序表的初始化构造一个空表,这对顺序表是一个加工型的运算,因此,将L设为指针参数,首先动态分配存储空间,然后将表中 last 指针置为-1,表示顺序表中没有数据元素。算法如下:

SeqList *init_SeqList(){ SeqList *L;L=malloc(sizeof(SeqList));L->last=-1;return L;
}

        设调用函数为主函数,主函数对初始化函数的调用如下: 

main(){SeqList *L;L=init_SeqList();…… 
} 

💦插入运算 :

        线性表的插入是指在表的第 i个位置上插入一个值为x 的新数据元素,插入后使原表长为 n的线性表 (ai, a2,…,ai-1,ai, ai+1,…,an) 成为表长为n+1 的线性表 (a1,a2,…,ai-1,x,ai, ai+1,…,an)。i的取值范围为1<=i<=n+1。
在顺序表上完成插入运算通过以下步骤进行:
将 ai~an顺序向后移动,为新数据元素让出位置;
将x置入空出的第i个位置;
修改 last 指针 (相当于修改表长),使之仍指向最后一个数据元素。
图B所示为顺序表中的插入运算示意图。

图B  顺序表中的插入运算示意图

☘️顺序表的数据元素的插入算法:

int Insert_SeqList(SeqList *L,int i,datatype x){int j;if(L->last==MAXSIZE-1){printf("表满");return -1;//表空间已满,不能插入 }if(i>L->last+2||i<1){//检查插入位置的正确性 printf("位置错");return 0; }	for(j=L->last;j>=i-1;j--){L->data[j+1]=L->data[j];//节点移动 } L->data[i-1]=x;//新数据元素插入 L->last++;//last指针仍指向最后一个数据元素 return 1;
}

        在本算法中应注意以下问题:

  • ⚡顺序表中数据区域有 MAXSIZE 个存储单元,所以向顺序表中插入新数据元素时应先检查表空间是否满了,在表满的情况下不能再进行插入运算,否则会产生溢出错误。
  • 要检验插入位置的有效性,这里i的有效范围是 1<=i<=n+1,其中 n 为原表长。
  • 注意数据的移动方向

        🔑插入算法的时间复杂度分析:顺序表的插入运算,时间主要消耗在数据的移动上,在第i个位置上插入X,从ai到an都要向后移动一个位置,共需要移动n-i+1个数据元素,而i的取值范围为1<=i<=n+1,即有n +1个位置可以插入。设在第i个位置上进行插入运算的概率为 pi,则平均移动数据元素的次数为:

        E_{in}=\sum_{i=1}^{n+1}p_{i}(n-i+1)

假设在第i个位置进行插入运算的可能性为等概率情况,即pi=1/(n+1),则 

E_{in}=\frac{1}{n+1}\sum_{i=1}^{n+1}(n-i+1)=\frac{n}{2}

这说明在顺序表上进行插入运算,需平均移动表中一半的数据元素。显然其时间复杂度为O(n). 

💦删除运算: 

        线性表的删除运算是指将表中第i个数据元素从线性表中去掉,删除后使原表长为 n的线性表 (a1,a2,…,ai-1,ai,ai+1,…,an)成为表长为n-1的线性表 (a1,a2,…,ai-1,ai+1,…,an),i的取值范围为1<=i<=n。
        在顺序表上完成删除运算的步骤如下:

将ai+1~an顺序向前移动;

修改 last 指针(相当于修改表长),使之仍指向最后一个数据元素。

图C所示为顺序表中的删除运算示意图。 

图C  顺序表中的删除运算示意图

☘️顺序表的数据元素的删除算法:

int Delete_SeqList(SeqList *L,int i){int j;if(i<1||i>L->last+1){printf("不存在第i个数据元素");return 0;//检查空表及删除位置的合法性 }for(j=i;j<L->List;j++){L->data[i-1]=L->data[i];//向上移动 }L->last--;return 1;//删除成功 
} 

        本算法注意以下问题:

⚡删除第i个数据元素,i的取值为1<=i<=n,否则第个数据元素不存在,因此,要检查删
除位置的有效性。

当表空时不能进行删除运算,因表空时 L->last 的值为-1,条件(1 >L->last+1)也包括了对表空的检查。 

删除a之后,该数据已不存在,如果需要,先取出 a,再进行删除。

        🔑删除算法的时间复杂度分析: 与插入运算相同,删除运算的时间主要消耗在移动表中数据元素上,删除第i个数据元素时,其后面的数据元素 ai+1~an都要向前移动一个位置,共移动了n-i 个数据元素,所以平均移动数据元素的次数为:

E_{de}=\sum_{i=1}^{n}p_{i}(n-i)

同样,在删除位置等概率情况下,pi=1/n,则

E_{de}=\frac{1}{n}\sum_{i=1}^{n+1}(n-i)=\frac{n-1}{2}

        这说明在顺序表上做删除运算时大约平均需要移动表中一半的数据元素。显然,该算法的时间复杂度为 O(n)。

💦按值查找 :

        线性表中的按值查找是指在线性表中查找与给定值 x 相等的数据元素。在顺序表中完成该运算最简单的方法是:从第一个数据元素 a 起依次和x进行比较,直到找到一个与x相等的数据元素,则返回它在顺序表中的存储下标或序号(二者差一);或者查遍整个表都没有找到与x相等的数据元素,返回-1。

☘️顺序表的数据元素查找算法:

int Location_SeqList(SeqList *L,datatype x){int i=0;while(i<=L.last&&L->data[i]!=x){i++;}if(i>L->last){return -1; }else return i;//返回的是存储位置 
}

        本算法的主要运算是比较,显然比较的次数与 x 在表中的位置有关,也与表长有关。当 a1=x时,比较一次成功。当 an=x 时,比较n 次成功。其平均比较次数为(n+1)/2,时间复杂度为 O(n)。 

三、顺序表的其他运算举例:

💦例1:

        将顺序表 (a1, a2,…,an)重新排列为以a1为界的两部分: a1前面的值均比 a1 小,a1后面的值均比 a1大(这里假设数据元素的类型具有可比性,不妨设为整型),运算前后如图D
所示。这一运算称为划分,a1称为基准。

        划分的方法有多种,下面介绍的划分算法思路简单,但性能较差

        基本思路:从第二个数据元素开始到最后一个数据元素,逐一向后扫描

当前数据元素 ai比 a1大时,表明它已经在 a1的后面,不必改变它与 a1之间的位置,继续比较下一个。

若当前数据元素比 a1小,说明它应该在 a1的前面,此时将它前面的数据元素依次向后移动一个位置,然后将它置于最前面。

☘️顺序表的划分算法: 

void partition(SeqList *L){int i,j;datatype x,y;x=L->data[0];//将基准置于x中 for(i=1;i<L->last;i++){if(L->data[i]<x){//当前数据元素小于基准 y=L->data[i];for(j=i-1;j>0;j--){//移动 L->data[j+1]=L->data[j];}L->data[0]=y;}}
} 

 💦例2:

        设有顺序表 A 和 B,其数据元素均按从小到大的顺序排列,将它们合并成一个顺序表 C,要求C的数据元素也从小到大排列。
        算法思路:依次扫描 A和B 的数据元素,比较 A、B 当前数据元素的值,将值较小的数据元素赋给 C,如此直到一个线性表扫描完毕最后将未扫描完顺序表中的余下部分赋给 C 即可。C的容量要能够容纳 A、B两个线性表长度的和。

☘️顺序表的合并算法:

void merge(SeqList A,SeqList B,SeqList *C){int i,j,k;i=j=k=0;while(i<=A.last&&j<=B.last){if(A.data[i]<B.data[i]){C->data[k++]=A.data[i++];}else{C->data[k++]=B.data[j++];}}while(i<=A.last){C->data[k++]=A.data[i++];}while(j<B.last){C->data[k++]=B.data[j++];}C->last=k-1; 
} 

上述算法的时间复杂度是 O(m+n),其中,m是A的表长,n是 B 的表长。 

 💦例3:

        两个线性表的比较运算。假定两个线性表的比较方法如下:设A、B 是两个线性表,表长分别为 m和n。A'和 B'分别为 A 和B 中除去最大共同前缀后的子表。例如,A= (x,y,y,z,x,z),B= (x,y,y,z,y,x,x,z),两表最大共同前缀为 (x,y,y,z)。则A'=(x,z),B'= (y,x,x,z)。若A'=B'=空表,则A=B。若A'=空表且B!=空表,或两者均不为空表且A'的首数据元素小于 B’的首数据元素,则A<B;否则,A>B。
        算法思路: 首先找出A、B 的最大共同前缀,然后求出 A'和 B,再按比较规则进行比较,A>B函数返回1;A=B 返回0:A<B 返回-1。

☘️两个顺序表的比较算法: 

int compare(int A[],int B[],int m,int n){int i=0,j,AS[],BS[],ms=0,ns=0;//AS,BS作为A'B' while(A[i]=B[i]){//找出最大共同前缀 i++;}for(j=i;j<m;j++){//求A',ms为A'的长度 AS[j-i]=A[j];ms++;}for(j=i;j<n;j++){//求B',ns为B'的长度 BS[j-i]=B[j];ns++;}if(ms==ns&&ms==0){return 0;}else if(ms==0&&ns>0||ms>0&&ns>0&&AS[0]<BS[0]){return -1;}else{return 1;}
}

上述算法的时间复杂度是 O(m+n)。 

四、总结: 

 学会顺序标的初始化,熟练掌握顺序表各种算法了基本结构。

五、共勉:

        以上就是我对线性表——(2)线性表的顺序存储及其运算的实现的理解,希望本篇文章对你有所帮助,也希望可以支持支持博主,后续博主也会定期更新学习记录,记录学习过程中的点点滴滴。如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对线性表的理解,请持续关注我哦!!! 

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

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

相关文章

A*算法学习

系列文章目录 前言 在总结 2023华为软件精英挑战赛——全赛段思路分享与总结 - 知乎 (zhihu.com)时&#xff0c;发现自己还有很多技术细节没搞懂&#xff0c;这里看静态全局路径规划最常见的A*算法&#xff0c;这个博主讲得很好&#xff1a; A-Star&#xff08;A*&#xff0…

【ASP.NET CORE】.NET 6.0 NET CORE MVC连接SQLSERVER数据库

项目装NuGet包&#xff0c;具体版本如下 在appsettings.json中&#xff0c;添加连接字符串 代码如下&#xff1a; "ConnectionStrings": {"MVCSqlContext": "Serverlocalhost;DatabaseAddress;User IDsa;Passwordsa;TrustServerCertificatetrue&q…

5 面试题--redis

伪客户端&#xff1a; 伪客户端的 fd 属性值为 -1&#xff1b;伪客户端处理的命令请求来源于 AOF ⽂件或者 Lua 脚本&#xff0c;⽽不是⽹络&#xff0c;所以这种客户端不需要套接字连接&#xff0c;⾃然也不需要记录套接字描述符。⽬前 Redis 服务器会在两个地⽅ ⽤到伪客户端…

BTCPay Server:免费、安全、开源的比特币支付处理器 | 开源日报 No.90

MunGell/awesome-for-beginners Stars: 58.0k License: NOASSERTION 这个项目是一个收集开源项目的列表&#xff0c;旨在帮助初学者找到可以贡献代码的机会。该列表按编程语言分类&#xff0c;并列出了每个项目以及其标签 (如 “good-first-issue”、“beginner” 等)。主要功…

Java小游戏 王者荣耀(简易版)

GameFrame类 所需图片&#xff1a; package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList…

Spring Cloud Stream如何屏蔽不同MQ带来的差异性?

引言 在当前的微服务架构下&#xff0c;使用消息队列&#xff08;MQ&#xff09;技术是实现服务解耦和削峰填谷的重要策略。为了保证系统的灵活性和可替换性&#xff0c;我们需要避免对单一开源技术的依赖。 市面上有多种消息队列技术&#xff0c;如 Kafka、RocketMQ、Rabbit…

对数据页的理解

1.InnoDB 是如何存储数据的&#xff1f; 数据表中的记录是按照行来存储的&#xff0c;但是数据库的读取并不以「行」为单位&#xff0c;否则一次读取&#xff08;也就是一次 I/O 操作&#xff09;只能处理一行数据&#xff0c;效率会非常低。 因此&#xff0c;InnoDB 的数据是按…

人工智能-优化算法之动量法

对于嘈杂的梯度&#xff0c;我们在选择学习率需要格外谨慎。 如果衰减速度太快&#xff0c;收敛就会停滞。 相反&#xff0c;如果太宽松&#xff0c;我们可能无法收敛到最优解。 泄漏平均值 小批量随机梯度下降作为加速计算的手段。 它也有很好的副作用&#xff0c;即平均梯度…

Android端极致画质体验之HDR播放

高动态范围HDR视频通过扩大亮度分量的动态范围(从100cd/m2到1000cd/m2)&#xff0c;以及采用更宽的色彩空间BT2020&#xff0c;提供极致画质体验。从Android10开始&#xff0c;支持HDR视频播放。 一、HDR技术 HDR技术标准包括&#xff1a;Dolby-Vision、HDR10、HLG、PQ。支持…

使用STM32 HAL库驱动烟雾传感器的设计和优化

STM32 HAL库是STMicroelectronics提供的针对STM32系列微控制器的一套硬件抽象层库&#xff0c;可以简化开发过程并提供对各种外设的支持。本文将介绍如何使用STM32 HAL库来驱动烟雾传感器&#xff0c;并对传感器数据采集和处理进行优化。将包括HAL库的初始化、模拟信号采集、数…

【开源视频联动物联网平台】流媒体传输协议HLS,FLV的功能和特点

HLS&#xff08;HTTP Live Streaming&#xff09;和FLV&#xff08;Flash Video&#xff09;都是用于视频流传输的协议或容器格式&#xff0c;但它们在某些方面有着显著的区别和特点。 HLS是一种由苹果公司开发的用于流媒体传输的协议&#xff0c;而FLV则是Adobe公司开发的用于…

SparkSQL远程调试(IDEA)

启动Intellij IDEA&#xff0c;打开spark源码项目&#xff0c;配置远程调试 Run->Edit Configuration 启动远程spark-sql spark-sql --verbose --driver-java-options "-Xdebug -Xrunjdwp:transportdt_socket,servery,suspendy,address5005"运行远程调试&#xf…