在数据结构中,顺序存储是指将数据元素按照逻辑顺序依次存储在一块连续的物理内存空间中,元素的物理地址与逻辑顺序直接相关(通常可通过公式计算元素地址)。这种存储方式的核心特点是 “连续存储、随机访问”,但插入 / 删除操作可能需要移动大量元素。
以下是典型的顺序存储数据结构,按常用程度和类型分类说明:
一、基础线性结构(核心顺序存储类型)
这类结构的逻辑关系是 “一对一”,且默认采用顺序存储实现(部分也支持链式存储,但顺序存储是其经典 / 首选方式)。
1. 数组(Array)
- 定义:最基础的顺序存储结构,将相同数据类型的元素存储在连续内存中,通过 “下标”(索引)快速访问元素。
- 顺序存储特性:
-
- 内存连续:所有元素占用一块不间断的内存空间,元素大小固定(如int占 4 字节)。
-
- 随机访问:已知数组首地址base和元素下标i,可通过公式 地址 = base + i * 元素大小 直接定位元素,时间复杂度为 O(1) 。
- 示例:
-
- 一维数组:int arr[5] = {1,2,3,4,5},内存中依次存储1(地址0x100)、2(0x104)、3(0x108)、4(0x10C)、5(0x110)(假设int占 4 字节)。
-
- 多维数组(如二维数组):本质是 “数组的数组”,仍采用顺序存储,分为 “行优先”(如 C/C++)或 “列优先”(如 Fortran)存储方式。例如int mat[2][3] = {{1,2,3},{4,5,6}},行优先存储顺序为1,2,3,4,5,6。
- 适用场景:需要频繁随机访问元素、元素数量固定或变化较少的场景(如存储学生成绩、矩阵数据)。
2. 顺序表(Sequential List)
- 定义:对数组的 “封装与优化”,是线性表的顺序存储实现。与数组不同,顺序表会记录 “当前元素个数” 和 “数组容量”,支持动态扩容(若为 “动态顺序表”)。
- 顺序存储特性:
-
- 底层依赖数组:仍使用连续内存,但增加了 “容量管理” 逻辑(如容量不足时申请更大的连续内存,拷贝原数据后释放旧空间)。
-
- 逻辑与物理顺序一致:元素的逻辑顺序(如第 1 个、第 2 个元素)与物理存储顺序完全相同。
- 与数组的区别:
| 对比维度 | 数组(Array) | 顺序表(Sequential List) |
|---|---|---|
| 容量管理 | 固定容量(静态),不可动态调整 | 支持动态扩容(动态顺序表) |
| 元素个数记录 | 不主动记录,需用户自行维护 | 内置size属性,直接获取个数 |
| 操作封装 | 仅提供下标访问,无默认操作 | 封装插入、删除、查找等接口 |
- 适用场景:需要线性表结构,且频繁访问元素、偶尔调整容量的场景(如实现简单的通讯录)。
3. 栈(Stack)- 顺序栈实现
- 定义:遵循 “先进后出”(LIFO)逻辑的线性结构,顺序存储实现的栈称为 “顺序栈”,底层依赖数组。
- 顺序存储特性:
-
- 固定栈底 / 动态栈顶:栈底指向数组首地址(固定不变),栈顶用指针 / 下标top标记(初始为 - 1,入栈时top++,出栈时top--)。
-
- 内存连续:所有栈元素存储在数组的连续空间中,栈满时需扩容(动态顺序栈)。
- 示例:顺序栈存储元素1,2,3的过程:
-
- 入栈1:top从 - 1 变为 0,数组 [0] = 1;
-
- 入栈2:top变为 1,数组 [1] = 2;
-
- 入栈3:top变为 2,数组 [2] = 3;
-
- 出栈:top变为 1,返回数组 [2] = 3。
- 适用场景:需要 “后进先出” 逻辑的场景(如函数调用栈、表达式求值、括号匹配)。
4. 队列(Queue)- 顺序队列实现
- 定义:遵循 “先进先出”(FIFO)逻辑的线性结构,顺序存储实现的队列称为 “顺序队列”,底层依赖数组。
- 顺序存储特性:
-
- 双指针管理:用front(队首,指向第一个元素)和rear(队尾,指向最后一个元素的下一个位置)标记队列范围,初始均为 0。
-
- 解决 “假溢出”:普通顺序队列会因rear到达数组末尾而无法入队(即使队首有空位),因此实际中常用 “循环顺序队列”(队尾满时,若队首有空位则绕回数组开头)。
- 循环顺序队列示例:数组容量为 5,存储元素1,2,3后出队1,再入队4:
-
- 初始:front=0, rear=0;
-
- 入队1,2,3:rear=3,数组 [0]=1, [1]=2, [2]=3;
-
- 出队1:front=1;
-
- 入队4:rear=(3+1)%5=4,数组 [4]=4(绕回数组末尾,避免假溢出)。
- 适用场景:需要 “先进先出” 逻辑的场景(如任务调度、消息队列、缓冲区处理)。
二、特殊顺序存储结构
这类结构的逻辑关系可能更复杂,但核心存储方式仍依赖连续内存。
1. 字符串(String)- 顺序存储实现
- 定义:由字符组成的线性序列,顺序存储是字符串的主流实现方式(如 C 语言的字符串)。
- 顺序存储特性:
-
- 连续存储字符:所有字符依次存储在连续内存中,末尾通常用特殊字符标记结束(如 C 语言的'\0')。
-
- 支持随机访问:与数组类似,可通过下标快速访问指定位置的字符(如str[2]获取第 3 个字符)。
- 示例:C 语言中字符串"abc"的存储:内存中依次为'a'(0x200)、'b'(0x201)、'c'(0x202)、'\0'(0x203)(每个字符占 1 字节)。
- 适用场景:文本处理、字符串匹配、数据传输等(如文件读写、网络通信中的字符数据)。
2. 顺序查找表(Sequential Search Table)
- 定义:用于 “查找操作” 的集合结构,将数据元素顺序存储在数组 / 顺序表中,查找时从表头到表尾依次比较元素。
- 顺序存储特性:
-
- 无排序要求:元素可无序存储(查找时需遍历所有元素,时间复杂度 O (n)),也可有序存储(支持二分查找,时间复杂度 O (logn))。
-
- 依赖连续内存:元素存储在连续空间中,便于遍历比较。
- 适用场景:数据量较小、查找频率较低的场景(如小型通讯录的查找)。
三、顺序存储的共性与局限性
理解顺序存储的核心特点,可帮助判断结构是否适用:
1. 共性(优势)
- 随机访问快:通过公式直接计算元素地址,访问时间复杂度 O (1)(数组、顺序表、顺序栈 / 队列均支持)。
- 存储密度高:无需额外空间存储指针(链式存储需),内存利用率高。
- 实现简单:底层依赖数组,逻辑清晰,代码易实现。
2. 局限性
- 插入 / 删除效率低:若在中间位置操作,需移动后续所有元素(如数组插入元素,时间复杂度 O (n))。
- 容量固定(静态) :静态顺序存储(如固定大小数组)无法动态扩容,易导致溢出;动态扩容需申请新内存、拷贝数据,开销较大。
- 内存要求高:需申请连续的大块内存,若内存碎片化严重,可能无法满足需求(链式存储可利用零散内存)。
综上,顺序存储是多种基础数据结构的核心实现方式,适用于 “频繁访问、少修改” 的场景,其设计思路也为后续复杂结构(如哈希表、树的数组实现)奠定了基础。