一、数据结构的基本概念
1.1 什么是数据结构
数据结构(Data Structure)是相互之间存在一种或多种特定关系的数据元素的集合,即数据的组织形式。它研究的不是数据本身,而是数据元素之间的相互关系,以及在这些关系上定义的操作。
1.2 数据结构的三要素
数据结构包括三个方面,缺一不可:
| 要素 | 研究内容 | 是否依赖计算机 |
|---|---|---|
| 逻辑结构 | 数据元素之间的逻辑关系 | 独立于计算机 |
| 存储结构(物理结构) | 逻辑结构在计算机存储器中的实现(映像) | 必须依赖计算机 |
| 数据的运算 | 对数据元素施加的一组操作及其实现 | 运算定义依赖逻辑结构,实现依赖存储结构 |
三者关系:逻辑结构是"设计图",存储结构是"落地实现",运算是"在这套结构上能做什么"。
二、数据的逻辑结构
2.1 逻辑结构的特点
- 独立于计算机,只描述数据元素之间抽象的逻辑关系;
- 与数据在计算机中如何存储无关;
- 由设计者根据业务需求赋予,是数据"本身应该是什么样"。
2.2 逻辑结构的完整分类
按数据元素之间关系的不同特性,逻辑结构分为四大类(经典分类法):
| 逻辑结构 | 元素间关系 | 关系特征 | 典型例子 |
|---|---|---|---|
| 集合 | 元素除"同属一个集合"外无其他关系 | 无特定关系 | 数学中的集合 |
| 线性结构 | 元素按一对一的次序排列 | 一对一 | 线性表、栈、队列、串 |
| 树形结构 | 元素之间有层次关系 | 一对多 | 二叉树、多叉树、堆 |
| 图状结构(网状结构) | 元素之间任意连接 | 多对多 | 有向图、无向图 |
2.3 常见逻辑结构的归类
| 分类 | 数据结构 |
|---|---|
| 线性结构 | 线性表、栈、队列、字符串(串)、一维数组 |
| 非线性结构 | 树、二叉树、图、多维数组、广义表、集合 |
需要特别辨析的两个结构
⚠️ 广义表不是简单的线性结构 广义表的元素既可以是原子,也可以本身是一个广义表(子表),是递归结构。当所有元素都是原子时它退化为线性表;当元素包含子表时,它可以表示树形等非线性结构。因此广义表是线性表的递归推广,不宜简单归为线性结构。
⚠️ 多维数组不是线性结构 一维数组是线性结构;二维及以上的多维数组,其元素下标具有多个维度,元素之间不是简单的一对一关系,通常归为非线性结构(或作为"数组结构"单独讨论)。
三、数据的存储结构
存储结构(物理结构)是逻辑结构在计算机存储器中的映像,必须依赖具体的计算机和语言。基本的存储结构有四种。
3.1 顺序存储
- 原理:把逻辑上相邻的元素存储在物理位置也相邻的存储单元中,元素间的逻辑关系由存储单元的邻接关系体现;
- 优点:支持随机存取(按下标 O(1) 访问)、存储密度高(无额外指针开销)、内存分配简单;
- 缺点:需要连续的内存空间、插入/删除需要移动大量元素(O(n))、可能产生外部碎片、扩容成本高。
3.2 链式存储
- 原理:不要求物理位置相邻,每个节点包含数据域和指针域,用指针表示元素间的逻辑关系;
- 优点:不要求连续内存空间、插入/删除只需修改指针(O(1),已知位置时)、动态增长灵活;
- 缺点:指针占用额外存储空间、只能顺序存取不能随机访问、缓存局部性差(节点分散)。
3.3 索引存储
- 原理 :在存储元素信息的同时建立附加的索引表,索引项由关键字和地址组成;
- 优点:检索速度快(可借助索引快速定位)、支持复杂查询;
- 缺点:索引表占用额外存储空间、增删元素时需要同步修改索引、维护成本高。
- 典型应用:数据库索引(B+ 树)、文件系统的索引节点。
3.4 散列存储(哈希存储)
- 原理 :根据元素的关键字,通过哈希函数直接计算出存储地址;
- 优点:理想情况下存取速度极快(O(1))、查找过程无需逐个比较;
- 缺点:存在哈希冲突(需要冲突处理机制)、不能反映元素间的逻辑关系、不支持顺序/范围访问、占用空间可能较大。
- 典型应用:哈希表、unordered_map。
3.5 四种存储结构对比
| 存储结构 | 物理相邻? | 随机存取? | 额外开销 | 典型数据结构 |
|---|---|---|---|---|
| 顺序存储 | ✅ 相邻 | ✅ 支持 | 无(密度最高) | 数组、vector |
| 链式存储 | ❌ 不相邻 | ❌ 不支持 | 指针空间 | 链表、树 |
| 索引存储 | 不要求 | 借助索引 | 索引表 | 数据库 B+ 树索引 |
| 散列存储 | 不要求 | 按哈希地址 | 哈希表空间 | 哈希表 |
同一种逻辑结构可以采用不同的存储结构。例如线性表,用顺序存储就是顺序表,用链式存储就是链表------逻辑结构相同,存储结构不同。
四、数据的运算
4.1 什么是数据的运算
运算是对数据元素所施加的一组操作的总称。
4.2 运算的两个层面
| 层面 | 内容 | 依赖于 |
|---|---|---|
| 运算的定义 | 规定运算"做什么"、运算的功能和输入输出 | 依赖于逻辑结构 |
| 运算的实现 | 运算"具体怎么做"、代码层面的执行 | 依赖于存储结构 |
例如"插入"运算:在线性表的逻辑结构上定义为"在第 i 个位置增加一个元素";但顺序存储通过移动元素实现,链式存储通过修改指针实现------定义相同,实现不同。
4.3 常见的基本运算
插入、删除、查找(检索)、修改、排序、遍历等。
4.4 抽象数据类型(ADT)
抽象数据类型(Abstract Data Type,ADT) = 数据对象(逻辑结构)+ 数据关系 + 定义在其上的一组操作。ADT 只描述"数据是什么、能做什么",把定义和实现分离,使用者只需要知道操作接口,不需要关心底层存储细节。
例如栈的 ADT 定义了 push/pop/top/isEmpty 等操作,底层用数组还是链表实现对使用者透明。
五、逻辑结构、存储结构与运算的关系
┌──────────────┐
│ 逻辑结构 │ 独立于计算机(设计者赋予)
│ (数据关系) │
└──────┬───────┘
│ 映像到
▼
┌──────────────┐
│ 存储结构 │ 依赖计算机(具体落地)
│ (物理实现) │
└──────┬───────┘
│ 决定
▼
┌──────────────┐
│ 运算实现 │ 定义依赖逻辑结构,实现依赖存储结构
└──────────────┘
核心结论:
- 逻辑结构决定"能定义哪些运算";
- 存储结构决定"这些运算如何高效实现";
- 同一逻辑结构 + 不同存储结构 = 不同的具体数据结构(如顺序表 vs 链表)。
六、从数据结构到 STL 容器
STL(标准模板库)中的容器,正是"逻辑结构 + 存储结构"不同组合的产物。
6.1 序列式容器
| STL 容器 | 逻辑结构 | 存储结构 | 说明 |
|---|---|---|---|
array |
线性表 | 顺序存储 | 固定大小数组(C++11) |
vector |
线性表 | 顺序存储 | 动态数组,尾部增删快、随机存取 |
deque |
线性表(双端) | 分段连续存储 | 中央 map 数组 + 多个缓冲区,头尾增删都 O(1) |
list |
线性表 | 链式存储 | 双向循环链表,任意位置增删 O(1) |
forward_list |
线性表 | 链式存储 | 单向链表(C++11),更省空间 |
6.2 容器适配器
⚠️ stack/queue/priority_queue 不是独立容器,而是容器适配器,它们包装一个底层容器实现特定接口。
| 适配器 | 逻辑结构 | 默认底层容器 | 说明 |
|---|---|---|---|
stack |
栈(LIFO) | deque | 后进先出 |
queue |
队列(FIFO) | deque | 先进先出 |
priority_queue |
堆(树形) | vector | 大顶堆(默认),每次取最大/最小元素 |
6.3 关联式容器(底层红黑树,树形结构)
| STL 容器 | 逻辑结构 | 存储结构 | 特点 |
|---|---|---|---|
set / multiset |
树(平衡二叉搜索树) | 链式存储 | 元素有序、不重复 / 可重复 |
map / multimap |
树(平衡二叉搜索树) | 链式存储 | 键值对、键有序、键不重复 / 可重复 |
底层都是红黑树,增删查 O(log n),元素按 key 有序排列。
6.4 无序关联容器(底层哈希表,散列存储)
| STL 容器 | 逻辑结构 | 存储结构 | 特点 |
|---|---|---|---|
unordered_set / unordered_multiset |
集合 | 散列存储 | 平均 O(1) 查找、无序 |
unordered_map / unordered_multimap |
集合(键值对) | 散列存储 | 平均 O(1) 查找、键无序 |
底层是哈希表(开链法),平均 O(1),最坏 O(n),元素无序。
6.5 逻辑结构 + 存储结构 → STL 完整映射
| 组合 | 对应 STL |
|---|---|
| 线性表 + 顺序存储 | array、vector |
| 线性表 + 分段连续存储 | deque |
| 线性表 + 链式存储 | list、forward_list |
| 栈/队列 + 适配底层容器 | stack、queue |
| 树形结构(堆)+ 顺序存储 | priority_queue |
| 树形结构(红黑树)+ 链式存储 | set、map、multiset、multimap |
| 集合 + 散列存储 | unordered_set、unordered_map |
| 字符串 + 顺序存储 | string、string_view |
七、核心总结
基本概念
- 数据结构三要素:逻辑结构、存储结构、数据的运算。
- 逻辑结构独立于计算机 ;存储结构依赖计算机 ,是逻辑结构的映像;运算定义依赖逻辑结构、实现依赖存储结构。
逻辑结构
- 四大逻辑结构:集合、线性结构(一对一)、树形结构(一对多)、图状结构(多对多)。
- 线性结构:线性表、栈、队列、串、一维数组;广义表是递归结构、多维数组是非线性结构,不能简单归为线性。
存储结构
- 四种存储结构:顺序存储(随机存取、需连续空间)、链式存储(指针、不连续)、索引存储(索引表)、散列存储(哈希函数、O(1))。
- 同一逻辑结构可以有多种存储实现(线性表 → 顺序表 / 链表)。
运算与 ADT
- 运算分"定义(做什么)"和"实现(怎么做)"两层;ADT 把定义与实现分离,只暴露操作接口。
STL 容器
- 序列式:vector/array(顺序)、deque(分段连续)、list/forward_list(链式);
- 适配器:stack、queue、priority_queue(包装底层容器,默认 deque/deque/vector);
- 关联式 :set/map(红黑树、有序、O(log n));无序关联:unordered_set/map(哈希表、平均 O(1)、无序)。