【数据结构】顺序表 | 详细讲解

news2025/3/17 1:43:31

在计算机中主要有两种基本的存储结构用于存放线性表:顺序存储结构和链式存储结构。本篇文章介绍采用顺序存储的结构实现线性表的存储。

顺序存储定义

线性表的顺序存储结构,指的是一段地址连续的存储单元依次存储链性表的数据元素。

线性表的(^{a_1{}}^{a_2{}}……^{a_n{}})的顺序存储示意图如下:

就比如说,在大学期间,我们同宿舍的有一个同学,人特别老实、热心,我们时常会让他帮我们去图书馆占座,他总是答应,你想想,我们一个宿舍连他共有九个人,这其实明摆着是欺负人的事。他每次一吃完早饭就冲去图书馆,挑一个好地儿,把他的书包里的书,一本一本地按座位放好,若书包里的书不够,他会把他的饭盒、水杯、水笔都用上,长长一排,九个座位硬是被他给占了,后来有一次因占座的事情弄得差点都要打架。

顺序存储方式

线性表的顺序存储结构,说白了,和刚刚的例子一样,就是在内存中找了块地,通过占位的形式,把一定的内存空间给占了,然后把相同数据类型的数据元素依次存放在这块空地中。既然线性表的每个数据元素的类型都相同,所以可以用C语言(其他语言也相同)的一维数组来实现顺序存储结构,即把第一个数据元素存到数组下标为0的位置中,接着把线性表相邻的元素存储在数组中相邻的位置。

线性表中,我们估算这个线性表的最大存储容量,建立一个数组,数组的长度就是这个最大存储容量。随着数据的插入,我们线性表的长度开始变大,不过线性表的当前长度不能超过存储容量,即数组的长度

数据长度与线性表长度区别

注意哦,这里有两个概念“数据的长度”和“线性表的长度”需要区分一下。

数组的长度是存放线性表的存储空间的长度,在静态分配的一维数组中,存储分配后这个量是一般不变的。但在动态分配的一维空间中,是可以实现动态分配数组。

线性表的长度是线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是变化的。

地址计算方法

由于我们数数都是从1开始数的,线性表的定义也不能免俗,起始也是1,可C语言中的数组是从0开始的,于是线性表的第i个元素是要存储在数组下标为i-1的位置,即数据元素的序号和存放它的数组下标之间存在对应的关系。

用数组存储顺序表意味着要分配固定长度的数组空间,由于线性表中可以进行插入和删除操作,因此分配的数组空间要大于等于当前线性表的长度。

存储器中的每一个存储单元都有自己的编号,这个编号称为地址。在前一个地址确定好之后,那么后面的位置都是可以计算的。由于每个数据元素,不管它是整形、实型还是字符型, 它都是需要占用一定的存储单元空间的。假设占用的是c个存储单元,那么线性表中第i+1个数据元素的存储位置和第i个数据元素的存储位置满足下列关系(LOC表示获得存储位置的函数)。

LOC(a_{i+1})=LOC(a_{i})+c

所以对于第i个数据元素a_{i}的存储位置可以由a_{1}推算得出:

LOC(a_{i})=LOC(a_{i})+(i-1)*c

地址计算公式,可以随时算出线性表中任意位置的地址,不管它是第一个还是最后一个,都是相同的时间。那么我们对每个线性表位置的存入或者取出数据,对于计算机来说都是相等的时间,也就是一个常数,因此用我们算法中学到的时间复杂度的概念来说,它的存取时间性能为O(1)。具有这一特点的存储结构称为随机存取结构。

顺序表的代码实现分析

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

顺序表一般可以分为:

  1. 静态顺序表:使用定长数组存储元素。
  2. 动态顺序表:使用动态开辟的数组存储。
//顺序表的静态存储
#define N 7
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType array[N];//定长数组
	size_t size;        //有效数据的个数
}SL;


//顺序表的动态存储
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* array; //指向动态开辟的数组
	size_t size;//有效数据个数
	size_t capacity;//容量空间的大小
}SL;

其实在平时以及以后的工作环境中,静态存储实践很少,因为不知道需要多少内存空间,N给大了不够用,N给小了浪费。静态顺序表只适合用于确定知道需要存多少数据的场景。所以在现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

顺序表的结构代码

typedef int SLDataType;
//顺序表的动态存储
typedef struct SeqList
{
	SLDataType* a; //指向动态开辟的数组
	size_t size;//有效数据个数
	size_t capacity;//容量空间的大小
}SL;

顺序表的初始化

在这个顺序表中有一个a数组,一个size是指有效数据的个数,一个capacity是容量空间的大小。对于size而言,size是有效数据的个数,而看作下标的话,那便是最后一个元素的下一个下标

在进行初始化是,要对数组以及顺序表结构体中的size以及capacity都进行初始化。

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

顺序表的销毁

起初,顺序表是一个空表,但是经过了一系列的增删改查之后就会存储一些数据,之后我们在销毁这个链表时,这个链表就是有内容的,所以在这里我们需要注意的是,销毁时需要断言一下这个顺序表是否为空,如果是空表的话,那还销毁什么。其次在销毁时,思路也很简单,无非就是让这个顺序表的几个变量都变为0。

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

顺序表的遍历打印

遍历打印的思路极其类似于数组的打印,利用下标即可。下标是从零开始的,且size是有效数据的个数即最后一个数据的下一个下标,所以这里的循环条件就是i<size。

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

顺序表的扩容 

因为在刚刚开始的时候,我们初始化size以及capacity都为0,所以为了防止每次扩容都是0,所以利用三目操作符来进行判断,如果这个capacity为0的话,那么就直接扩容4字节,其他的都是扩容它的两倍。

void SLCheckCapacity(SL* psl)
{
	assert(psl);
	int Newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
	SLDataType* tmp = (SLDataType*)realloc(psl->a, Newcapacity * sizeof(SLDataType));
	if (tmp == NULL)
	{
		perror("realloc fail\n");
	}
	psl->a = tmp;
	psl->capacity = Newcapacity;
}

顺序表的尾插

在添加数据时,不论是前插后插还是在指定位置插入,我们都需要为其判断是否有足够的空间,也就是判断是否需要扩容。扩容之后将数据插入顺序表中。

void SeqListPushBack(SL* psl, SLDataType x)
{
	assert(psl);
	SLCheckCapacity(psl);
	psl->a[psl->size - 1] = x;
	psl->size++;
}

这里注意一下,在尾插之后需要将size++。 

顺序表的头插

顺序表的头插相比较尾插就较难一些,因为需要将所有的数据都向后挪动一位。所以得再借助一个指针用来遍历。

void SeqListPushFront(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++;
}

顺序表的尾删

尾删还是比较容易的,无非就是将size--,但是这里还要断言一下确保size是大于零的。

void SeqListPopBack(SL* psl)
{
	assert(psl);
	assert(psl->size > 0);
	psl->size--;
}

顺序表的头删

如果想要头删的那话,就是将后面的元素给覆盖到前一个,从前往后开始。

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

顺序表的查找

顺序表查找,在这个顺序表中是否有这个数组。其实这个也就是遍历,很简单利用下标。

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

顺序表在pos位置插入x的数

这里我们需要断言一下这个是否为有效位置。 

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

顺序表删除pos位置上的值

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

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

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

相关文章

Activiti BPMN visualizer Using Of Idear

Launch 安装插件 创建文件 可视化创建按钮 设置条件,是在线上设置的

【C++破局】C++内存管理之new与deleted剖析

​作者主页 &#x1f4da;lovewold少个r博客主页 ⚠️本文重点&#xff1a;c内存管理部分知识点梳理 &#x1f449;【C-C入门系列专栏】&#xff1a;博客文章专栏传送门 &#x1f604;每日一言&#xff1a;花有重开日&#xff0c;人无再少年&#xff01; 目录 C/C的内存分配机…

Vue中的常用指令v-html / v-show / v-if / v-else / v-on / v-bind / v-for / v-model

前言 持续学习总结输出中&#xff0c;Vue中的常用指令v-html / v-show / v-if / v-else / v-on / v-bind / v-for / v-model 概念&#xff1a;指令&#xff08;Directives&#xff09;是Vue提供的带有 v- 前缀 的特殊标签属性。可以提高操作 DOM 的效率。 vue 中的指令按照不…

Java Web——HTTP协议

目录 1. HTTP协议概述 1.1. HTTP数据传输格式 1.2. HTTP协议特点 2. HTTP 1.0和HTTP 1.1 3. HTTP请求协议 3.1. GET方式请求协议 3.2. POST方式请求协议 3.3. GET请求和POST请求的区别 4. HTTP相应协议 4.1. 响应状态码 如果两个国家进行会晤需要遵守一定的礼节。所以…

WMS配送中心主要业务流程

业务流程图 入库 波次出库 按门店和门店所属送货路线确定出库波次 入库 出库 移库、封仓 门店欠货能要点 1. 日常补货&#xff1a;分拣仓位商品小于当前商品在该位置的补货下限的时候&#xff1b;生成对此进行补货任务&#xff1b;补货完成后确认任务&#xff0c;系统变更库存…

win10使用mingw安装OpenCV4.8

1. cmake安装 下载链接如下https://github.com/Kitware/CMake/releases/download/v3.27.7/cmake-3.27.7-windows-x86_64.zip 解压后放到指定目录后&#xff0c;添加bin目录到环境变量即可。 2. mingw安装 下载链接如下(下图的x86_64-posix-sjlj)&#xff1a; Download x86_…

DevChat:提升编程效率的AI编程助手

一、前言 1、当前开发的痛点&#x1f616; 在软件开发过程中&#xff0c;开发者经常需要编写复杂的代码&#xff0c;如数据结构、算法、网络通信等&#xff0c;这些都需要耗费大量的时间和精力。同时&#xff0c;不同的编程语言和框架也会给开发者带来许多不便&#xff0c;例如…

Hadoop入门——数据分析基本步骤

文章目录 1.概述2.分析步骤2.1第一步 明确分析目的和思路2.2第二步 数据收集2.3第三步 数据处理2.4第四步 数据分析2.5第五步 数据展现2.6第六步 报告撰写 3.总结 1.概述 2.分析步骤 2.1第一步 明确分析目的和思路 2.2第二步 数据收集 2.3第三步 数据处理 2.4第四步 数据分析 …

C语言每日一题(28) 反转链表

牛客网 BM1 反转链表 题目描述 描述 给定一个单链表的头结点pHead(该头节点是有值的&#xff0c;比如在下图&#xff0c;它的val是1)&#xff0c;长度为n&#xff0c;反转该链表后&#xff0c;返回新链表的表头。 数据范围&#xff1a; 0≤n≤1000 要求&#xff1a;空间复…

数据分析实战 | SVM算法——病例自动诊断分析

目录 一、数据分析及对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型训练 八、模型应用及评价 一、数据分析及对象 CSV文件——“bc_data.csv” 数据集链接&#xff1a;https://download.csdn.net/download/m0_70452407/88…

【C++】智能指针(一)

这篇文章介绍下C的智能指针&#xff0c;当然&#xff0c;可能没有你想的那么智能。 为什么需要智能指针1 void remodel(string& str) {string* ps new string(str);str *ps;return; }这里不讨论这个函数有没有意义&#xff0c;在这段代码中&#xff0c;很明显&#xff…

arduino 简易智能花盆

编辑器&#xff1a;arduino IDE 主板&#xff1a;arduino uno 传感器&#xff1a; 0.96寸的OLED屏&#xff08;四脚&#xff09; 声音模块 土壤温湿度模块 DS18B20温度模块&#xff08;这里用到防水的&#xff09; 光敏电阻模块&#xff08;买成三脚的了只能显示高低&#x…

state 和 props 有什么区别?

一、state 一个组件的显示形态可以由数据状态和外部参数所决定&#xff0c;而数据状态就是 state&#xff0c;一般在 constructor 中初始化 当需要修改里面的值的状态需要通过调用 setState 来改变&#xff0c;从而达到更新组件内部数据的作用&#xff0c;并且重新调用组件 r…

Linux操作系统下对c语言程序的编译和执行过程gcc,编译链接过程

目录 1.gcc,g,gdb安装 2.gcc分布编译链接 3.编译链接过程 4.执行 4.1 如何执行 4.2 两步执行与一步执行 4.3 多文件的编译执行 1.gcc,g,gdb安装 命令行写gcc,g,gdb根据提示安装:sudo apt install gcc/g/gdb 2.gcc分布编译链接 (1)预编译: gcc -E main.c -o main.i (2)…

视频剪辑技巧:探索画中画视频剪辑,如何制作引人入胜的视觉效果

在视频制作领域&#xff0c;画中画视频剪辑是一种备受瞩目的技术&#xff0c;它可以将多个视频画面叠加在一起&#xff0c;形成一种独特的视觉效果。这种剪辑技巧可以让观众同时看到两个或多个视频片段&#xff0c;创造出一种引人入胜的视觉体验。在开始画中画视频剪辑之前&…

C++算法:矩阵中的最长递增路径

涉及知识点 拓扑排序 题目 给定一个 m x n 整数矩阵 matrix &#xff0c;找出其中 最长递增路径 的长度。 对于每个单元格&#xff0c;你可以往上&#xff0c;下&#xff0c;左&#xff0c;右四个方向移动。 你 不能 在 对角线 方向上移动或移动到 边界外&#xff08;即不允…

工商业微电网储能盈利方式研究笔记

1. 光储微电网 1.1. 关于光储微电网 光储微电网可以看成是一组由分布式光伏、储能装置、本地负荷组成的包括发、输、配、用管理系统在内的小型局域电网&#xff0c;并通过唯一的公共连接点接入大电网&#xff0c;既可以并网运行也可以独立运行。 发展分布式光储微电网的意义…

STM32 X-CUBE-AI:Pytorch模型部署全流程

文章目录 概要版本&#xff1a;参考资料STM32CUBEAI安装CUBEAI模型支持LSTM模型转换注意事项模型转换模型应用1 错误类型及代码2 模型创建和初始化3 获取输入输出数据变量4 获取模型前馈输出模型应用小结 小结 概要 STM32 CUBE MX扩展包&#xff1a;X-CUBE-AI部署流程&#xf…

ROS 多级tf坐标转换

题目 现有一移动机器人&#xff0c;该机器人的基坐标系为“base_link”&#xff0c;机器人包含3个子坐标系分别为“joint1”&#xff0c;“joint2”&#xff0c;“joint3”。 要求&#xff1a;利用多坐标转换&#xff0c;实现joint1下的坐标向joint2下的坐标转换&#xff0c;…

AMD64内存属性详解

本文参考文档为AMD64 Architecture Programmer’s Manual Volume 2: System Programming&#xff0c;版本号3.41&#xff0c;这不是对原英文文档的翻译&#xff0c;但是所有内容的排版都是根据原手册的排版来的&#xff0c;如有与官方文档冲突的内容&#xff0c;以官方文档为准…