Skip to content

数据结构

前言

程序 = 数据结构 + 算法。 前面我们学了 CPU 如何执行指令、操作系统如何管理资源。但程序要处理的核心对象是数据——用户信息、商品列表、社交关系……这些数据怎么在内存里组织,直接决定了程序的快慢。你可能遇到过这样的困惑:为什么有些程序处理几万条数据很快,有些处理几百条就卡住了?答案往往就在于数据结构的选择

这篇文章会带你学什么?

学完这章后,你将获得:

  • 直觉判断力:看到一个需求,脑子里自动浮现该用什么数据结构
  • 性能分析视角:能判断性能瓶颈是数据结构选错了,还是算法效率低
  • 权衡思维:理解"空间换时间"与"时间换空间",知道没有完美的数据结构
  • 代码阅读能力:看到 HashMap、Stack、Queue 这些词不再陌生
  • 后续学习基础:为数据库索引、缓存系统、搜索引擎等技术打下基础
章节内容核心概念
第 1 章全景图四大类数据结构、分类标准
第 2 章线性结构数组、链表、栈、队列
第 3 章哈希表哈希函数、冲突处理、O(1) 查找
第 4 章树形结构二叉树、文件系统树、DOM 树
第 5 章图结构有向图、无向图、遍历算法
第 6 章性能对比时间复杂度、空间复杂度
第 7 章选型指南场景分析、决策流程

1. 全景图:数据结构是什么?

想象你要整理一堆书:

  • 堆在地上:找书要一本本翻——这就是最原始的存储
  • 按编号放书架:直接去对应位置拿——这就是数组
  • 按类别分柜子:先确定柜子再找书——这就是哈希表
  • 按书名排序放多层架:每次排除一半——这就是

不同的整理方式,找书的效率天差地别。数据结构就是数据的"整理方式"——它决定了数据怎么存、怎么找、怎么改。

数据结构全景图不同场景选择不同的数据组织方式
数据结构就像整理房间的方式:把衣服放进衣柜、书放在书架、杂物放抽屉
📚
线性结构
数据按顺序排列,像一排书
数组链表队列
🗂️
哈希结构
通过关键词快速查找
哈希表字典集合
🌳
树形结构
层级关系,像家谱
二叉树B 树
🕸️
图结构
复杂关系网络
有向图无向图网络图
📚线性结构
特点
数据元素之间一对一关系
有明确的先后顺序
可以是连续存储或链式存储
适用场景
📝
数组:列表数据
存储学生成绩、商品价格等有序数据
🔄
栈:撤销操作
文本编辑器的撤销功能,后进先出
🎫
队列:任务调度
打印队列、任务队列,先进先出
操作复杂度
操作平均时间
访问元素O(1)
插入/删除O(n)
生活类比
像一列火车,车厢按顺序连接
要找到第 5 节车厢,直接数过去;要插入新车厢,需要断开连接

所有数据结构可以归为四大类:

类型数据关系典型代表生活类比
线性结构一对一,排成一排数组、链表、栈、队列火车车厢、排队队伍
哈希结构键→值映射哈希表、字典、集合图书馆索引卡片
树形结构一对多,层级关系二叉树、B树、堆家族族谱、文件夹
图结构多对多,网状关系有向图、无向图地铁线路图、社交网络

为什么要学这么多种?

因为没有万能的数据结构。每种结构都是在"查找速度"、"插入速度"、"内存占用"之间做权衡。就像你不会用书包装家具,也不会用卡车送一封信——选对工具,事半功倍。


2. 线性结构:最基础的组织方式

线性结构是最直觉的数据组织方式——数据一个接一个排列,就像火车车厢。但"怎么连接"和"从哪端操作"的不同,产生了四种变体,各有各的绝活。

线性结构的四种形态数组、链表、栈、队列的区别
数组连续内存,编号访问
0
10
1
25
2
33
3
47
4
59
5
62
✓ 连续内存存储 | ✓ 快速访问 (O(1)) | ✗ 插入删除慢 (O(n))
操作对比
数据结构访问插入删除特点
📊 数组O(1) 快O(n) 慢O(n) 慢大小固定
🔗 链表O(n) 慢O(1) 快O(1) 快大小可变
🥞 栈O(n)O(1) 快O(1) 快一端操作
🚶 队列O(n)O(1) 快O(1) 快两端操作
实际应用场景
📋
列表数据
学生成绩、商品价格列表
🖼️
图像处理
像素矩阵存储
📈
统计图表
按时间顺序的数据

2.1 数组 vs 链表:两种截然不同的存储方式

数组和链表是最基础的两种线性结构,它们的核心区别在于内存布局

对比维度数组链表
内存布局连续的一整块散落在各处,用指针串起来
访问第 n 个直接算地址,O(1)从头一个个找,O(n)
中间插入后面的都要挪,O(n)改两个指针就行,O(1)
大小创建时就固定了随时可以增长
生活类比一排编号储物柜寻宝游戏的线索链

什么时候用数组?什么时候用链表?

  • 数据量已知、频繁按位置访问 → 数组(比如学生成绩表、像素矩阵)
  • 数据量未知、频繁插入删除 → 链表(比如播放列表、撤销历史)
  • 不确定? → 先用数组。大多数场景下,数组的缓存友好性带来的性能优势更大

2.2 栈和队列:加了"规矩"的线性结构

栈和队列本质上就是数组或链表,只是限制了操作方式。看起来功能变少了,但正是这种限制让它们有了明确的用途:

结构规则操作类比你写的代码里在哪?
后进先出 (LIFO)push / pop一摞盘子函数调用栈、浏览器后退、Ctrl+Z 撤销
队列先进先出 (FIFO)enqueue / dequeue排队买票任务调度、消息队列、打印队列

为什么"限制"反而是好事?

想象一个只有"放盘子"和"拿盘子"两个操作的栈——你永远不会拿错顺序。限制带来确定性,确定性带来可靠性。 函数调用栈就是靠"后进先出"保证最后调用的函数最先返回,如果允许随意访问中间的函数,程序就乱套了。


3. 哈希表:最快的查找

线性结构的查找都不够快——数组要遍历 O(n),即使排好序用二分查找也要 O(log n)。有没有一种结构能做到 O(1) 直接找到?有,就是哈希表。

哈希表:超快的查找通过关键词直接找到数据
📚
哈希表就像图书馆的索引卡片:不用在一排排书架上找,直接查索引就能找到书的位置
存储数据
哈希过程
输入键
apple
哈希函数
hash(key) % 10
数组索引
0
哈希表
0
apple: 苹果
1
2
3
4
5
6
orange: 橙子
7
8
9
banana: 香蕉
性能对比
哈希表查找
O(1)
瞬间找到
数组查找
O(n)
需要遍历
二分查找
O(log n)
需要排序
常见应用
👤
用户信息表(用户ID → 用户资料)
🛒
购物车(商品ID → 数量)
📝
缓存系统(URL → 网页内容)
🔍
字典(单词 → 释义)

3.1 哈希表的核心思想

哈希表的原理其实很简单:

  1. 你给一个(比如 "apple")
  2. 哈希函数把键算成一个数字(比如 hash("apple") = 3
  3. 直接去数组的第 3 个位置找——不用遍历,一步到位

这就像图书馆的索引系统:你不用在一排排书架上找,查索引卡片就能直接定位到书的位置。

3.2 哈希冲突:两个键撞车了怎么办?

两个不同的键可能算出同一个索引——这叫哈希冲突。就像两本书的索引号相同,都指向同一个位置。

解决方法原理类比
链地址法同一位置用链表存多个值同一个柜子里放多本书
开放寻址法冲突了就往后找空位柜子满了就放隔壁柜子

3.3 哈希表的性能

操作平均情况最坏情况(全部冲突)
查找O(1)O(n)
插入O(1)O(n)
删除O(1)O(n)

什么时候会退化?

当所有键都映射到同一个索引时,哈希表退化为链表,所有操作变成 O(n)。避免方法:选择好的哈希函数 + 动态扩容(负载因子超过阈值时扩容)。

哈希表在你的代码里无处不在

  • JavaScript 的 {} 对象和 Map → 哈希表
  • Python 的 dict → 哈希表
  • Java 的 HashMap → 哈希表
  • 数据库的索引 → 底层也用哈希

你每次写 user["name"]map.get("key"),背后都是哈希表在工作。


4. 树形结构:层级关系的表达

哈希表查找快,但数据是无序的。如果你需要既能快速查找,又能保持数据有序,就需要树形结构了。

树的核心特征:每个节点可以有多个"孩子",但只有一个"父亲"(根节点除外)。这种一对多的层级关系,在现实中随处可见。

树形结构:层级关系的表示像家谱一样的组织方式
选择树的类型:
50307020406080
树形结构的特点
🌲
层级关系
节点之间是一对多的父子关系
🎯
单一根节点
除根节点外,每个节点只有一个父节点
🔍
高效查找
二叉搜索树的查找时间是 O(log n)
🔄
多种遍历
前序、中序、后序、层序遍历
应用场景
📁
文件系统
文件夹和文件的层级组织
🌐
HTML DOM
网页元素的嵌套结构
🏢
组织架构
公司的管理层级关系
🌲
决策树
机器学习的分类算法

4.1 二叉搜索树:有序的树

二叉搜索树有一个简单但强大的规则:左小右大

  • 左子树的所有值 < 根节点
  • 右子树的所有值 > 根节点

查找时,每次比较都能排除一半节点,时间复杂度 O(log n)。就像猜数字游戏——"比 50 大还是小?""大。""比 75 大还是小?"——每次排除一半。

4.2 平衡树:防止退化

二叉搜索树有个问题:如果数据按顺序插入(1, 2, 3, 4, 5),树会退化成一条链,查找变回 O(n)。平衡树通过自动调整结构来避免这个问题:

类型平衡策略特点典型应用
AVL 树严格平衡(高度差 ≤ 1)查找最快,插入删除稍慢需要频繁查找的场景
红黑树近似平衡综合性能好Java TreeMap、Linux 内核
B 树多路平衡,一个节点存多个值减少磁盘 I/O数据库索引

树在你的代码里在哪?

  • 文件系统:文件夹嵌套就是树结构
  • HTML DOM<html><body><div><p> 就是一棵树
  • 数据库索引:B+ 树让百万级数据的查找只需要 3-4 次磁盘读取
  • JSON/XML:嵌套的数据格式本质上就是树

5. 图结构:复杂关系的网络

树只能表示"一对多"的层级关系。但现实中很多关系是"多对多"的——你的朋友也有朋友,城市之间有多条路可以走。这种任意节点之间都可能有连接的结构,就是图。

图结构:复杂关系的表示节点和边的网络
ABCDE
图的特点
节点 (V)
5
边 (E)
6
2.4
应用场景
🗺️地图导航(最短路径)
👥社交网络(好友关系)
🌐网页链接(PageRank)
🔗依赖关系(包管理)

5.1 图的三种形态

类型特点类比典型应用
无向图边没有方向,A→B 等于 B→A微信好友(互相的)社交网络、通信网络
有向图边有方向,A→B 不等于 B→A微博关注(单向的)网页链接、依赖关系
带权图边有权重(距离、费用等)城市间的公路(有里程数)地图导航、最短路径

5.2 图的遍历

图的遍历比线性结构复杂,因为可能有环(A→B→C→A),需要记录"已访问"的节点:

遍历方式策略类比适用场景
BFS(广度优先)先访问所有邻居,再访问邻居的邻居水波纹扩散最短路径、层级遍历
DFS(深度优先)一条路走到底,走不通再回头走迷宫路径搜索、连通性判断

图在现实中的应用

  • 地图导航:城市是节点,道路是边,导航就是在图上找最短路径
  • 社交网络:用户是节点,关注/好友是边,"你可能认识的人"就是图算法推荐的
  • 包管理器:npm/pip 的依赖关系就是有向图,npm install 就是在做图的拓扑排序

6. 性能对比:一张表看清所有数据结构

学了这么多数据结构,它们的性能到底差多少?下面这个交互式对比能帮你建立直觉:

数据结构:数据的"容器"不同场景选择不同的存储方式
数组连续内存,索引访问快
010
120
230
340
450
560
670
780
访问 arr[2] = O(1),插入/删除 = O(n)
时间复杂度对比
操作数组链表哈希表
访问O(1)O(n)O(1)O(log n)
查找O(n)O(n)O(1)O(log n)
插入O(n)O(1)O(1)O(log n)
删除O(n)O(1)O(1)O(log n)
核心思想:数据结构是数据的"容器",不同的容器有不同的特点。选择合适的数据结构,能让程序效率提升几个数量级。

核心性能对比表:

数据结构访问查找插入删除空间
数组O(1)O(n)O(n)O(n)O(n)
链表O(n)O(n)O(1)O(1)O(n)
栈/队列O(n)O(n)O(1)O(1)O(n)
哈希表O(1)O(1)O(1)O(n)
二叉搜索树O(log n)O(log n)O(log n)O(n)
O(V+E)O(1)O(E)O(V+E)

怎么读这张表?

  • O(1):不管数据量多大,操作时间恒定——最快
  • O(log n):数据量翻倍,时间只多一步——很快
  • O(n):数据量翻倍,时间也翻倍——一般
  • O(V+E):取决于节点数和边数——图的特殊表示

注意:这些都是平均情况。最坏情况下,哈希表会退化到 O(n),二叉搜索树也会退化到 O(n)。


7. 选型指南:该用哪种数据结构?

学了这么多数据结构,面对实际需求时该怎么选?关键是从需求出发,问自己几个问题:

  1. 最频繁的操作是什么? 查找?插入?删除?遍历?
  2. 数据之间有什么关系? 一对一?一对多?多对多?
  3. 数据量有多大? 几十条和几百万条的最优选择可能完全不同
  4. 需要有序吗? 是否需要按某种顺序遍历数据
如何选择合适的数据结构?根据场景需求做出最佳选择
你的使用场景是?
🔍
快速查找
根据关键词快速找到对应数据
📊
保持顺序
数据需要按插入顺序或特定顺序存储
🥞
后进先出
最后进入的最先处理
🚶
先进先出
先来的先处理
🌳
层级关系
数据之间有父子层级关系
🕸️
复杂关系
数据之间有多对多的复杂连接
快速参考表
场景需求推荐数据结构时间复杂度
随机访问数组O(1)
快速查找哈希表O(1)
有序查找二叉搜索树O(log n)
频繁插入删除链表O(1)
撤销操作O(1)
任务调度队列O(1)
选择决策流程
需要快速访问元素?
数组 / 哈希表
需要频繁插入删除?
链表
需要保持顺序?
栈 / 队列
树 / 图

快速决策流程:

你的需求推荐结构原因
按位置快速访问数组O(1) 随机访问
频繁在中间插入删除链表O(1) 插入删除,不用移动元素
后进先出(撤销、递归)LIFO 语义天然匹配
先进先出(任务队列)队列FIFO 语义天然匹配
按键快速查找哈希表O(1) 平均查找
有序数据 + 快速查找二叉搜索树O(log n) 查找且保持有序
复杂多对多关系能表达任意节点间的连接

实际开发中的经验法则

  • 80% 的场景用数组和哈希表就够了
  • 需要有序时考虑树
  • 关系复杂时考虑图
  • 不确定? 先用最简单的,遇到性能问题再换。过早优化是万恶之源

总结

数据结构是程序的骨架。数组像一排编号储物柜,按位置取东西最快;链表像寻宝线索链,插入删除最灵活;哈希表像图书馆索引,按名字找东西最快;像家族族谱,表达层级关系且保持有序;像地铁线路图,表达任意复杂的网状关系。没有最好的数据结构,只有最合适的——关键是理解每种结构的优势和代价,根据实际需求做出权衡。


延伸阅读

主题推荐资源
数据结构可视化VisuAlgo - 动画演示各种数据结构和算法
算法与数据结构《算法图解》- Aditya Bhargava,图文并茂适合入门
深入理解《数据结构与算法分析》- Mark Allen Weiss
刷题练习LeetCode - 按数据结构分类练习

下一步

现在你已经掌握了数据结构的核心知识。接下来可以继续学习:

  • 算法思维:学会用排序、搜索、递归、动态规划等算法思维解决问题
  • 编程语言:了解不同编程语言如何实现这些数据结构