尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

数据结构核心解析:从数组链表到哈希表与排序查找的完整链路

发布时间:2026/9/29 16:13:27

资讯中心
01
ARTICLE

数据结构核心解析:从数组链表到哈希表与排序查找的完整链路

数据结构核心解析:从数组链表到哈希表与排序查找的完整链路
你是不是也干过这种事——看到网上到处都在推“数据结构”是程序员必修课转头就去搜了“数据结构PDF”一口气下载了五六本然后从严蔚敏那个经典的C语言版第一页开始看。结果第一句话就把人劝退了“数据结构是相互之间存在一种或多种特定关系的数据元素的集合。”这句话信息密度不低但如果你还没有写过几百行代码根本不知道它在说什么。我当年被这句话劝退过三次一直到工作后才真正想明白数据结构教的不是“结构”是你在回答“这堆数据该怎么搁才能增删改查都又快又省”这个问题时手里握着的几张底牌。这篇东西我想按自己的方式把这门课讲清楚。不按课本目录的顺序堆概念而是从“它到底解决什么问题”出发把数组、链表、栈、队列、哈希表、树、图、排序查找这些核心内容串成一条完整的链路。不管是初学者、在准备数据结构实验报告、还是备考408数据结构考研应该都能从中捞到一点有用的东西。1. 先搞明白一件事数据结构教的不是“结构”是存取策略1.1 为什么这门课劝退了那么多人很多教材的讲法是从定义出发。但定义这个东西没有足够的编程体感根本转化不成能力。我后来给学生讲课时喜欢换一个说法数据结构就是“你把一堆数据放进内存然后反复对它做增删改查到底怎么放才划算”的学问。知识点归纳到最后你会发现整门课只回答四个问题数据怎么组织、怎么存进内存、用户操作怎么高效地做、代价有多大。用个生活例子类比。图书馆有藏书你可以把所有书堆在大厅里找书的时候一本一本地翻顺序查找也可以按照索书号排上架找的时候用二分法迅速定位有序表二分查找还可以在入口放一张“关键词到书架位置”的索引卡哈希表/索引更可以按学科分类一层层展开像文件夹一样树结构。这些方案没有谁绝对高级关键看读者的主要操作是什么查找多还是上架多、数据量多大、要不要按顺序输出。数据结构整门课锻炼的就是你面对这种问题时做出判断的能力。1.2 三件套逻辑结构、存储结构和操作先理清三个层次不然你会发现概念越看越乱。第一层叫逻辑结构描述数据元素之间的抽象关系。关系只有几类线性结构一对一像排队、树形结构一对多像目录、图形结构多对多像社交网络、集合元素之间没有特殊关系。逻辑结构只看“关系”不关心它在内存里长什么样。第二层叫存储结构也就是在计算机里实际怎么放。顺序存储用连续内存链式存储用指针串起来索引存储额外建一张索引表散列存储按哈希函数算出存放位置。同一个逻辑结构可以有不同的存储实现一个存储实现也可以服务不同的逻辑结构。第三层叫做操作就是建、增、删、改、查、遍历这六个基本动作。拿一个通讯录来贯穿这三个层次。从逻辑上看通讯录是一个线性表一串联系人。如果用一个数组存放联系人记录是顺序表如果每个节点里存一个指针指向下一个联系人地址是链表如果按姓氏首字母建一张ABCD索引表是索引存储如果按电话号码直接计算存储位置是哈希存储。很多人背了一堆定义却不会做题根源就是把这三个层次混在一起。1.3 复杂度是选型的“比价器”学数据结构最关键的技能不是记住结构定义而是学会估算代价。估算的尺子叫时间复杂度和空间复杂度。复杂度不是“程序跑了多少秒”那是性能测试的结果复杂度描述的是“当数据规模n变大的时候操作代价怎么增长”。这几个符号是最常用的O(1)表示不管n多大操作时间基本恒定O(n)表示数据量翻倍时间也翻倍O(log n)表示数据量翻倍时间只增加一个固定的量。正因为有O(log n)这种性质二分查找在十万条数据和一亿条数据里都几乎一瞬间完成。有个简单估算办法数循环的嵌套层数。一层循环完整处理数据集通常是O(n)两层循环就是O(n²)每轮循环能把数据规模砍掉一半就是O(log n)。这个技能练熟之后后面学排序、查找、树和哈希表你都用得上。很多同学代码能跑通但不会分析复杂度其实就是没有建立“按操作模式估算”的思维。2. 数组与链表两种存储原语的选择逻辑数组和链表是后面所有结构的“地基”。栈可以用数组实现也可以用链表实现队列、树、图、哈希表统统都要基于这两种存储方式。所以这两兄弟必须彻底吃透。2.1 数组连续内存带来的“随机访问特权”数组最核心的杀手锏是随机访问。为什么a[i]能一步到位因为数组在内存里占据一段连续空间每个元素大小相同所以第i个元素的地址直接等于起始地址加i乘以元素大小。这个计算一条指令就完成了。CPU和操作系统也特别喜欢这种局部性——你访问了某个元素附近的数据大概率会被一起加载进缓存下次访问速度极快。代价是插入和删除。在数组中间插入一个元素后面所有元素都得往后挪一位最坏O(n)。如果数组长度不够还得重新申请一块更大的内存把旧数据复制过去这就是扩容。拿电影院打比方连排座位看演出的时候最好的加座或挪人都麻烦。严蔚敏教材里的顺序表很多人觉得无聊但它实际上是一大堆题目的基础——考研题型里“计算数组某个元素的存储地址”就是从这里来的。2.2 链表用指针把散落的节点串起来链表是另一种极端。节点分散在内存的各个角落每个节点除了存数据还要存一个指针指向下一个节点单链表只存next双向链表再存一个prev。用C语言定义就是typedef struct Node { int data; struct Node *next; } Node;这样一个节点无论待在内存哪个位置都无所谓只要前一个节点保存着它的地址链条就不缺。好处是插入和删除只需要改相邻节点的指针坏处是访问第k个元素必须从头一个个跳过去。数组是“你告诉我编号我直接拉抽屉”链表是“我拿着线索一张纸条一张纸条地找”。链表这个结构有个不算坑的坑都说“链表插入是O(1)”但严格讲单链表的插入要先花O(n)时间找到目标位置才能O(1)改指针。真正O(1)的是“在已知节点后面插入”。不少面试题专门埋伏在这个点别上钩。2.3 什么时候该用谁一张对比表就够了操作需求数组链表随机访问a[i]优O(1)劣O(n)尾部插入删除优O(1)摊还优O(1)头部或中间插入删除劣O(n)搬移优但要先定位O(n)内存与缓存友好优连续内存劣节点跳来跳去扩容劣要复制旧数据优动态生长理论上说完看工程实践。Java里ArrayList和LinkedList之争绝大多数情况下大家选ArrayList因为随机访问场景多而且缓存友好。Redis的list对象早期实现是双端链表但为了省内存又引入了压缩列表编码纯链表只是它的一种形态。这背后说明的是同一个道理选什么结构永远取决于你的操作画像。不分析操作就谈结构优劣都是耍流氓。3. 栈、队列、哈希表工程里天天在用的三个结构这三兄弟在教材里排在数组链表后面但它们在真实的工程代码里出现频率远高于树和图。操作系统、浏览器、编辑器、消息中间件甚至数据库索引到处是它们的身影。3.1 栈递归、撤销、括号匹配的老家栈是一个操作受限的线性表只允许在同一端栈顶插入和删除所以天然是后进先出。叠盘子的模型就是栈后放上去的盘子最先被拿走。为什么递归函数能一层层返回因为每次函数调用系统会在调用栈上压入一个栈帧里面保存着局部变量、参数和返回地址函数return时栈帧弹出。深度递归一崩爆栈本质就是栈帧太多把内存空间占满了。我讲递归时总提醒学生递归不是免费的魔法每一次递归都是一次入栈。所以递归改循环很多时候就是在手动管理一个栈。栈的经典练习题是括号匹配。遇到左括号入栈遇到右括号弹栈并检查是否匹配最后栈空说明所有括号都合法。这个题目思路清晰非常适合拿来做“第一次用数据结构解决实际问题”的入门训练。LeetCode上“有效括号”和“最小栈”也都是这个结构的经典验证。3.2 队列先进先出和图的BFS队列的逻辑跟食堂排队一样先到先服务。操作系统里的进程调度队列、打印任务队列、消息中间件的消费队列全是这个思想。用数组实现队列时有一个经典设计叫循环队列。因为如果只用两个下标front和rear入队出队会让数组头部的空间被浪费所以干脆让下标在逻辑上绕成一个环(rear 1) % maxSize用这个算式判断队满。模运算一行代码却解决了“数组空间怎么循环复用”这个核心问题非常漂亮。队列最让人“恍然大悟”的应用是广度优先遍历BFS。先把起点入队每次弹出队首节点把所有未访问过的邻居入队。因为先进先出所以遍历一层之后必然先进入下一层正好达到“按层扫描”的效果。很多同学学队列时不知道为什么还要学BFS等到学图的时候回头看才发现工具早就给好了。3.3 哈希表空间换时间的“快捷键”哈希表是这些结构里最“反直觉”的。它用一个哈希函数把key映射成数组下标理想情况下一次计算直接定位目标读写的平均复杂度逼近O(1)。难点在于冲突。不同的key可能算出同一个下标这就是哈希冲突。现在工业界最常见的解法是链地址法每个桶里挂一个链表冲突的元素挂到同一个链上。最坏的情况是所有元素都分到同一个桶哈希表退化成链表O(1)变成O(n)。因此设计者会通过负载因子元素数/桶数控制风险超过阈值就扩容申请更大的桶数组把所有老元素重新哈希一遍。这个“重新哈希”的开销很大但均摊下来每个元素仍然很快。几乎所有编程语言的字典、map、哈希集合底层都是这套机制。Python的dict、Java的HashMap都是。数据分析里熟悉的pandas它的DataFrame看着像魔法底层其实也是多个一维数组按列组织配合索引结构来加速查找——而索引加速的底层无非就是哈希表或树。理解了这层你对“为什么很多工具都强调索引”就会有本质认识。工程上还有一个非常经典的组合LRU缓存用哈希表加双向链表实现。哈希表负责快速定位key双向链表维护访问顺序淘汰时删除链表尾节点即可。这告诉我们一个更重要的规律真正复杂的工程结构往往是几个基础结构的组合拳。4. 树和图从线性思维到层级与网络的跃迁4.1 树为什么无处不在线性结构像一根线把数据串成一串。但现实世界大量关系是层级化的公司组织架构、电脑的文件系统、网页的DOM节点树、图书分类法都是树。树的定义是每个节点有零个或多个孩子但只有一个父节点根节点除外。二叉树的概念看似很多核心就几个满二叉树每层都满完全二叉树从根到倒数第二层全满、最后一层左侧连续。而二叉搜索树BST的定义是“左小右大”任意节点的左子树所有值都小于它右子树所有值都大于它。有了这个性质查找一个值时每走一步就能排除一半的节点所以平衡的BST查找是O(log n)。你可以把它理解为“长了指针的二分查找”。那为什么会出现AVL树、红黑树这么多变种原因很直白如果按顺序往里插入数据BST会退化成一根斜链查找退化成O(n)。平衡树的核心思想就是每次插入删除后检查是否还“够平衡”不平衡就旋转调整。旋转看着复杂本质只是“在不破坏左小右大的前提下改变树形”理解到这个程度就已经赢过大多数人。4.2 遍历是递归的最佳练习二叉树的前序、中序、后序遍历是每一本教材都会安排的固定内容。三种遍历的递归写法极其对称# 中序遍历左 - 中 - 右 def inorder(root): if not root: return inorder(root.left) print(root.val) inorder(root.right)背不住顺序没关系记住“输出的位置就是‘中’的位置”即可。前序是先输出再递归左右中序是递归完左子树再输出后序是左右子树都递归完才输出。表达式求值里逆波兰表达式跟树的后序遍历天然对应所以解析表达式类的问题总爱和树扯上关系不是没有原因的。4.3 图关系复杂后存储和遍历都要换思路图是最后一道坎。节点之间的连接是任意的可以有环可以有多个父节点。图的存储最常用两种邻接矩阵用二维数组i行j列是1表示有边邻接表则是每个顶点存一个链表链表里放邻居。邻接矩阵查询两点之间是否有边是O(1)但空间O(n²)邻接表空间只有O(ne)但查一条边要遍历链表。自己没有直观感受的话想想一个上万节点的社交网络——你得先决定它是体现“稠密”还是“稀疏”然后才能选存储方式。图的两个经典问题是遍历和最短路径。深度优先遍历DFS用递归写起来和树的先序遍历本质相同广度优先遍历BFS就是第三章讲的队列应用因为BFS一层层扩展所以无权图的最短路径可以直接用它。带权图就要上Dijkstra这种贪心算法了。学完这一章你会发现前面所有的结构——栈、队列、递归、邻接表——都是后面图算法的零件。数据结构就是这么一环扣一环。408考研里图属于重概念、轻代码的板块难度不及树但邻接矩阵和邻接表的转换、DFS/BFS的时间复杂度计算是高频考点别轻易跳过。5. 排序与查找数据结构最好的“实战演习”5.1 简单排序理解比较和移动的代价很多人觉得排序是“算法课”的内容跟数据结构关系不大。但我一直认为排序是数据结构最佳的练习场它把“数组操作 递归 复杂度分析”全串在一块儿了。一套排序学下来你对“什么是O(n²)”“什么是分治”“为什么递归要控制深度”都会产生直观体感。三个O(n²)的简单排序各有各的“人格”冒泡排序两两比较大的往后沉每轮把最大的数送到底部选择排序每轮扫描找到最小值和开头交换插入排序像整理扑克牌把新牌插到已排好序列的正确位置。谁是最实用的我多说一句插入排序。虽然平均复杂度高但在近乎有序的数据上它表现极好所以很多高级排序在数据规模小时会切回插入排序兜底——Java的Arrays.sort、Python的Timsort里都有这个设计。新手做这三个排序最大的收获是弄懂“比较次数和移动次数分别怎么数”“最好情况和最坏情况为什么不一样”。5.2 快排、归并、堆排O(nlogn)时代快速排序是“分治”思想的代表选一个基准pivot把比它小的放左边比它大的放右边然后递归处理左右两边。平均O(nlogn)最坏O(n²)——最坏的情况就是每次基准都选得特别不巧。工程上会用“三数取中”或“随机化基准”来压制最坏情况。快排最大的优点是原地分区不需要额外的大数组所以空间消耗极低。这也是它比归并排序更受标准库欢迎的直接原因。归并排序的最大价值在于稳定并且天然适合链表和外部排序。链表没法随机访问快排的partition在链表上很别扭归并排序通过“递归分到只剩一个节点再两两按序合并”反而如鱼得水。堆排序则需要一个“堆”这个数据结构把数组看作完全二叉树自底向上调整堆性能稳定在O(nlogn)且不需要额外空间但因为常数大、对缓存不友好工程上出场率比快排低。我在学习这部分的时候给过学生一个很土但有效的建议把下面这张表记住让它变成条件反射。排序算法平均时间最坏时间空间稳定性冒泡O(n²)O(n²)O(1)稳定选择O(n²)O(n²)O(1)不稳定插入O(n²)O(n²)O(1)稳定快排O(nlogn)O(n²)O(logn)不稳定归并O(nlogn)O(nlogn)O(n)稳定堆排O(nlogn)O(nlogn)O(1)不稳定5.3 二分查找和BST动态查找的经典模型查找跟排序是堂兄弟。顺序查找O(n)二分查找O(logn)哈希查找平均O(1)。二分查找听起来简单但left right还是left right、mid该不该加1减1是无数人写错过的经典。我的建议是自己手写一遍然后用三个边界用例去测只有一个元素、只有两个元素、目标不存在。测完你就知道边界条件有多毒了。另一个送分细节mid不要写成(left right) / 2最好写成left (right - left) / 2防止left和right太大时整数溢出。这个习惯在王道数据结构和其他考研资料里都反复强调过考场上也极可能考。那二叉搜索树和二分查找是什么关系数组做二分查找虽然快但插入删除要搬移元素。BST是把“中间值”放在根节点的动态结构插入和查找都沿着树边走边判断所以BST就是支持增删的二分查找。到了哈希表查找直接变成O(1)但代价是数据不再有序。选哪个说到底还是要回到那个问题你到底需不需要“按顺序遍历”。6. 学习数据结构的路线与排坑指南6.1 PDF看得再多不如亲手写一遍链表网上的“数据结构PDF”一抓一大把严蔚敏版、王道版、各种笔记满天飞。但说句实话数据结构是那种“你以为你会了但其实根本没会”的科目。因为它的知识点太依赖手上的感觉。链表的指针到底改没改对、递归栈帧怎么弹、快排的partition到底交换了哪些元素——这些光靠看文字过十分钟就忘。我个人的建议是教材当参考代码必须自己敲。严蔚敏的《数据结构C语言版》有个特点很多代码是“伪代码级”的风格偏学术你需要自己动手把它补成能跑起来的程序。当年我学链表前老老实实用C语言从头写了单链表的所有操作建表、插入、删除、反转。这一遍走完“指针即引用”这个概念就再也不会忘。之后再学二叉树再用递归实现三种遍历。这个过程不轻松但这是「数据结构实验报告」里最锻炼人的环节。如果你在用“头歌”这类实验平台也是一回事——平台会把任务拆成一步步但真正吸收多少取决于你是把它当填空游戏还是当代码工程来对待。6.2 期末考与408考研的备考姿势数据结构在期末考试和考研里考的核心不是代码量而是概念是否准确、复杂度是否会算、外加少量代码填空。408数据结构部分常考点高度集中线性表的顺序与链式实现栈和队列的出入序列分析树的性质计算叶子节点数与度的公式图遍历、最小生成树、最短路径各类排序算法的稳定性与时间复杂度哈希表的构造、冲突处理与查找长度计算。复习这类内容我认为最有用的不是翻书而是列“导图 表格”。把每个结构的时间复杂度汇总成一张大表贴墙上每天扫一遍。比如查找顺序O(n)、二分O(logn)、哈希平均O(1)排序那张表上面已经给了。这些数字必须到“条件反射”级别因为考场没有时间让你现场推。至于代码题键盘上必须能默写的内容其实非常有限链表反转、二叉树前中后序遍历、快排partition、二分查找、DFS/BFS框架。把这几段练到滚瓜烂熟比背一百道题目更见效。6.3 资源的搭配与我的学习顺序建议关于入门的顺序我自己带学生的时候是这么安排的你可以参考先建立体系选一本教材从头到尾按章节推进严蔚敏C语言版或王道数据结构都行不要求啃完每次不会的细节先搭框架。配一个可视化网站比如Visualgo去看看“插入排序到底怎么交换的”“红黑树到底是怎么旋转的”。可视化对建立空间感的帮助是文字无法替代的。刷题用LeetCode或洛谷简单题面试方向重点刷链表反转、相交、环和栈队列括号、单调栈这些题最检验你对底层是否真懂。学有余力再看语言专属的数据结构比如C的STL容器、Java集合框架、Python的内置类型。你这时候会发现哈希表、红黑树、循环队列早就被标准库封装好了学数据结构的意义在于——你知道什么时候该用哪个以及最坏会坏到什么程度。结合我个人的授课经验每次学完一个结构主动问自己两个问题这个结构适合什么操作它和上一个结构相比增加了什么、又损失了什么这两个问题想清楚了你脑子里那张“数据结构地图”才算真正建立起来。最后分享一个个人习惯把复杂度那张表打印出来贴在电脑边上写代码前瞟一眼。过一段时间选型就不再需要翻书了。如果你正在准备数据结构实验报告或者考前冲刺按这个顺序去整理会比从第一页翻到最后一页舒服很多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。