news 2026/10/10 9:50:29

数据结构1基础入门:掌握数据组织思维与复杂度分析,少走弯路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据结构1基础入门:掌握数据组织思维与复杂度分析,少走弯路

数据结构这门课,网上资料多到让人眼花缭乱,但真正能把“数据结构1”的基础打扎实的人,其实并不算多。很多人上来就啃《数据结构》教材,被C语言的指针和递归劝退,或者直接用Python刷题,刷到最后发现连复杂度都不会算。作为一个把数据结构从本科一路用到工作、又带过不少新人入门的老兵,我想把这门课第一阶段最该搞明白的东西,掰开揉碎讲一遍。这篇文章适合正在学数据结构、准备期末复习、或者刚开始准备考研408的同学,内容不追求大而全,只求把“数据怎么组织、算法怎么评价、代码怎么写才稳”这三件事讲透,让你少走弯路。

1. 先想清楚:数据结构这门课到底在训练什么

1.1 数据结构学的不是“代码”,是“组织数据的思维”

很多初学者会把数据结构和“写代码”划等号,觉得学链表就是背一下struct Node怎么写,学二叉树就是记一下递归遍历的代码。这个理解不能说错,但太浅了。数据结构这门课真正训练的,是你面对一组数据时,能够根据操作需求设计出合理存储方式的能力。说白了,它研究的是“数据怎么放”和“数据怎么用”之间的匹配关系。

举个例子,如果你要维护一个“随时从头部插入、尾部删除”的队列,数组和链表哪个更合适?答案看起来很简单,但背后的原因值得想清楚:数组在头部插入需要搬移所有元素,时间复杂度是O(n),而链表只需要改两个指针,O(1)就能搞定。这种“存储方式决定了操作成本”的思维,才是数据结构的核心。很多人在刷题时发现“思路对了但超时”,问题往往不是算法本身,而是底层数据结构选错了。

我见过不少用Python刷题的同学,上来就是一个list走天下,增删改查全用它。list的尾部添加很快,但头部插入要移动整个数组,数据量一上来就慢得离谱。这时候如果知道用collections.deque,底层是双向链表,情况就完全不一样了。这就是“数据结构1”阶段最需要建立的意识:代码是表象,数据结构才是决定性能的骨架。

1.2 为什么考研、面试、竞赛都在卡数据结构

不管是考研408的数据结构部分,还是大厂面试的手撕代码,又或者是ACM竞赛,数据结构都是避不开的一关。原因其实不复杂:数据结构是最能反映一个人“计算思维”水平的考察点。给定一个场景,你能不能快速判断该用哪种结构?给你一段代码,你能不能分析出它的时间复杂度和空间复杂度?这比单纯背几百道算法题更能看出功底。

往实际了说,数据库的索引为什么用B+树而不是链表?操作系统的进程调度为什么用优先级队列?浏览器的前进后退为什么是两个栈而不是一个数组?这些看起来“高深”的系统设计,底层全是数据结构的基础知识。所以“数据结构1”并不是一门孤立的课程,它是后续操作系统、数据库、计算机网络等课程的共同前提。这就是为什么考研408把它放在最前面,也是为什么面试官喜欢从链表反转、栈实现队列这类题目开始热身。

当然,学习压力也随之而来。每年期末都有大量学生在“树的遍历”和“图的最短路径”上栽跟头,考研人则普遍在排序算法的稳定性比较和折半查找的边界条件上丢分。这些坑并不是因为你笨,而是因为教材往往把“结论”直接给你,却没有告诉你“为什么”。我写这篇文章,就是想把这些“为什么”补上。

1.3 语言选型:C版、Python版到底该怎么选

很多人在学数据结构前会纠结:用C语言学还是Python学?这其实是个很实际的问题。我的建议是分情况对待。

如果你是计算机专业,要应付考试和考研,那大概率逃不开C语言版。C语言的指针、结构体、动态内存分配,能把“内存布局”这件事暴露得非常彻底。写一个链表,你需要自己malloc、自己管理next指针、自己注意野指针,痛苦是真的痛苦,但收获也是真的实在。很多用Java或Python写数据结构的同学,可能真没想过“节点”在内存里到底长什么样,而C语言强迫你想清楚。

如果你主要目的是快速掌握算法思想、应对面试手写代码,那Python其实更好用。Python的语法省掉了大量繁琐的内存管理细节,让你能把精力集中在“结构怎么组织”上。而且Python原生提供的list、dict、deque已经内置了很多数据结构,你甚至可以在不自己写链表的情况下先理解链表的思想。但要注意,Python版本的教材或网课,通常默认你已经会Python基础语法,直接讲逻辑,对纯新手不算友好。

我这里还要提一个容易被忽略的点:无论你选C还是Python,最终都要回到“自己能独立实现”这个标准上。教材上的代码是别人的,你抄一遍再跑通,那只是最低级的“会了”。真正的高手,会合上书,自己从零把链表、栈、二叉树写一遍,再写一遍,直到不卡壳。语言只是表达工具,数据结构设计的思路才是可以迁移到任何语言里的技能。我个人更建议“C语言打底、Python辅助验证”:用C把结构写扎实,再用Python快速验证思路,两者互补,效果很好。

2. 从线性结构入门:数组、链表、栈与队列

2.1 数组与链表:第一个分水岭

数据结构的第一大板块,几乎都是线性结构,也就是数据元素之间存在“一对一”的逻辑关系。线性表最典型的两种实现就是顺序表(数组)和链表。这一块是“数据结构1”的分水岭,很多人从这开始掉队,因为链表涉及指针操作,对“内存地址”的直观感要求很高。

数组(顺序表)的优势是随机访问:给我一个下标,我直接通过首地址加偏移量算出来,O(1)访问。缺点是插入和删除通常需要移动元素,平均O(n)。链表则相反:要访问第k个节点,你必须从头一个个next过去,O(n);但只要你拿到了某个节点的指针,在它后面插入一个新节点,只需要改两个指针,O(1)。这就是两种结构在“读”和“写”上的取舍。

真正的学习重点,不是背下这些结论,而是亲手用C语言实现一遍单链表。我建议按这个顺序练:定义节点结构体、创建链表(头插法和尾插法)、遍历打印、按值查找、按位置删除、链表反转。链表反转看似简单,实际上一半初学者第一次写都会陷入“指针丢了”的问题。核心技巧是:在改变当前节点的next之前,先用一个临时指针保存下一个节点的地址,否则你改完next就找不到后面的节点了。

Python代码看起来简单很多,但有一个细节值得注意:Python的变量本质是引用,node = node.next这种操作实际上是在“移动引用指针”,理解了这一点,Python链表和C语言链表在原理上就没区别了。很多Python初学者写链表时容易写出node = node.next之后又去操作旧节点,结果发现节点丢失,其实就是引用赋值没想清楚。

2.2 栈与队列:两个“不讲道理”的线性结构

栈和队列在逻辑上都属于“操作受限的线性表”,也就是说,它们不允许你随意访问中间元素,只能在特定的位置进行插入和删除。栈是后进先出(LIFO),只在栈顶操作;队列是先进先出(FIFO),队尾入队、队头出队。这个限制不是缺点,反而让它们在某些场景下特别好用。

栈最常见的应用是函数调用:每调用一个函数,就把局部变量压入调用栈,函数返回时再弹出。所以如果你写了一个没有终止条件的递归函数,系统会报“栈溢出”错误——因为调用栈被无限压满了。理解了这一点,“数据结构1”里的递归,就不只是“函数自己调用自己”这种表面说法,而是一种使用系统栈的行为。

队列的应用就更生活化了:打印机任务排队、消息队列、CPU任务调度,全是队列的影子。在实现上,队列如果用数组实现,要注意“假溢出”问题。简单说,就是元素出队后,数组前面的空间空了出来,但队尾指针已经走到数组末尾,没法再入队新元素了。解决办法是循环队列,让队尾指针绕回数组头部继续用。这个知识点几乎年年出现在期末卷或者考研选择题里,值得用纸笔画一画循环队列的入队、出队过程,把(rear+1)%MAXSIZE这个“取余”操作彻底搞清楚。

栈还有一个很有意思的点:它能解决“括号匹配”问题。思路很简单:遇到左括号就入栈,遇到右括号就弹出栈顶检查是否匹配,最后栈为空说明完全匹配。这个题目用C和Python都可以练习,代码量不大,但对“什么时候用栈”会有非常直观的体会。

2.3 字符串匹配:从暴力到KMP的思维跃迁

字符串在“数据结构1”里往往被单独列为一章,核心操作是模式匹配:在一个主串里查找子串出现的位置。最朴素的方法是暴力匹配,也就是从主串的每个位置开始,依次和模式串比较,不匹配就移一位再来。这个算法好理解,但最坏情况的时间复杂度是O(n*m),也就是主串长度乘以模式串长度,效率很低。

KMP算法是这个阶段的第一个“劝退知识点”。它的核心思想是:匹配失败时,不回溯主串指针,而是利用已经匹配的信息,把模式串尽量往右移动,然后继续匹配。这里的关键是求next数组,也就是模式串每个位置之前的“最长相同前后缀长度”。很多教材这块写得特别绕,但如果你换个角度理解就会舒服很多:next[i]本质上是在告诉你“如果第i位匹配失败,模式串应该跳到哪个位置重新匹配”。

我自己学KMP的时候,花了整整一个下午手算next数组。我的建议是:不要急着看代码,先拿几个具体的模式串,比如ABABACA,手动把每个位置的前后缀长算出来,再跟着代码走一遍匹配过程。一旦你理解了“主串不回溯、模式串滑动”这个画面,KMP就不再是玄学了。期末如果考KMP,大概率也就是让你算next数组,或者分析它的时间复杂度O(n+m),这都属于理解了就不丢分的题目。

3. 树与图:从“一对一”到“一对多”的进阶

3.1 二叉树:递归思维的训练场

讲完线性结构,课程就进入非线性结构阶段,第一个主角就是树。树结构用来表达“一对多”的关系,比如文件目录、组织结构、家族谱系。在所有树中,二叉树因为有“每个节点最多两个子节点”这个约束,结构最规整,也最容易用递归处理,所以教材通常重点讲解二叉树。

二叉树的关键操作几乎全部围绕遍历展开:前序(根左右)、中序(左根右)、后序(左右根)、层序。前三者用递归实现,每段代码只有几行,看起来极其简单,但初学者普遍反映“看懂了,自己写不出来”。原因在于递归的思维模式还没建立。我教新人的方法是:先不写代码,用手模拟“打印根节点之后去处理左子树,再处理右子树”的流程,在一棵具体的小二叉树上走一遍,看打印顺序是否符合预期。等你不需要看代码也能说出某个遍历顺序的下一步时,再用代码表达就顺理成章了。

除了遍历,二叉树还有几个必须掌握的考点:根据中序+前序(或中序+后序)重建二叉树、求二叉树深度、判断两棵树是否相同、判断平衡二叉树。这些题目都有一个共同点——适合用递归“分而治之”。它们考察的形象化理解是:“整棵树的深度 = 左子树深度和右子树深度的较大值 + 1”。如果你真的理解了这句递归公式,深度的代码你会自己写出来,根本不用背。

层序遍历则相对特殊,它不天然适合递归,而需要借助队列来实现:根节点入队,然后循环“出队一个节点、访问它、把它的左孩子和右孩子入队”,直到队列为空。这个过程特别像“按层推进”,很多画图题、手动模拟题都会考。建议亲手画一个三层二叉树,用队列模拟一遍层序遍历,整个流程就刻在脑子里了。

3.2 二叉搜索树与平衡树:为什么有序这么重要

二叉搜索树(BST)是一个带有“有序性”的二叉树:对任意节点,它的左子树所有节点都小于它,右子树所有节点都大于它。带来的好处是,查找、插入、删除的平均时间复杂度可以到O(log n),这比线性表的O(n)快了一个量级。这也是为什么计算机科学里“有序”这件事如此重要。

但二叉搜索树有一个致命问题:如果插入顺序恰好像1,2,3,4,5这样递增,树就会退化成一条“斜链”,查找复杂度直接变成O(n),跟链表没区别。为了解决这个问题,出现了各种平衡树,比如AVL树、红黑树。在数据结构1阶段,重点通常放在AVL树的四种旋转上:LL、RR、LR、RL。很多人在这一步觉得“特别绕”,我的经验是:别急着记忆旋转代码,先用图形理解“失衡节点是哪三个”,LL就是直线型往左偏,RR就是直线型往右偏,LR和RL是折线型,要先变成直线再旋转。画图比看代码更有用。

平衡树的旋转确实抽象,但它在现实中特别重要。数据库索引、std::map、std::set、Java的TreeMap,底层都离不开平衡树思想。所以在学习阶段多花点时间,后面接触高级主题时会轻松很多。而且期末考试特别喜欢出“根据插入序列构造AVL树”的模拟题,只要你能熟练画出旋转过程,这类题基本等于送分。

3.3 图的基本操作与遍历:抓住“邻接”这个关键

图是比树更一般的结构,表达的是“多对多”关系,比如社交网络的好友关系、地图上的道路网络、课程之间的先修关系。图的存储方式主要有两种:邻接矩阵和邻接表。

邻接矩阵就是个二维数组,matrix[i][j]表示顶点i到顶点j是否有边。它实现简单,判断两个顶点是否直接相连只要O(1),但缺点是空间占用大,如果是稀疏图(边很少),会浪费大量内存。邻接表则对每个顶点维护一个链表,链表中存储它所有相邻的顶点,空间利用率高,但判断两个顶点是否相邻就要遍历链表,会慢一些。这个“时间换空间、空间换时间”的取舍,是数据结构里最常见的思维训练。

图的遍历有两种经典策略:深度优先搜索(DFS)和广度优先搜索(BFS)。DFS用递归或栈实现,能深入一条路径走到底再回头,就像走迷宫时一路摸墙走;BFS用队列实现,一层层往外扩展,就像水波扩散。二者的区别,很多同学到期末都没理清:DFS看重“路径探索”,BFS看重“层次扩展”。特别值得注意,求“无权图的最短路径”时,用BFS就很自然,因为BFS第一次访问到目标节点时,走的路径一定是最短的,这是由“逐层扩展”的天然特性决定的。

这一章的难点不仅在于代码,更在于模型抽象。比如“课程先修关系”可以建模成有向图,“能否完成全部课程”就是判断图里有没有环;“朋友圈有几块连通区域”就是计算连通分量个数。能把这些应用场景和代码对应起来,图的章节才算真正入门。

4. 查找与排序:算法效率的最直观体现

4.1 折半查找:一道例题看清“有序”的价值

查找是最常见的操作之一,顺序查找逐个比较,O(n);如果数据有序,就可以用折半查找,把复杂度降到O(log n)。折半查找也叫二分查找,思路是:每次取中间位置的值与目标比较,如果目标比中间值小,就只在左半部分继续找;如果大,就在右半部分找;相等就命中。

折半查找例题是期末和考研的高频考点,最典型的考法就是给一个有序数组和要查找的关键字,让你写出查找过程中依次比较的下标。很多同学在这上面丢分,不是不会二分,而是对“边界条件”不够严谨。最常见的错误是:循环条件写成while(left < right)还是while(left <= right)分不清;中间位置取(low+high)/2还是(low+high+1)/2搞不明白。

我提供一个经过大量验证的理解方式。如果你用闭区间[low, high]来表示搜索范围,那循环条件就应该写low <= high,因为当low == high时,这个位置还没有被比较过,需要进入循环。如果写成low < high,最后这一个元素就会被漏掉。在计算mid时,通常取low + (high - low) / 2,这样可以防止low+high相加溢出(虽然考试手算一般不在乎,但写成这种形式是一种好习惯)。如果查找失败,low的最后位置正好就是“应该插入的位置”,这个性质在求解“寻找插入位置”的题目时非常有用。

折半查找的“有序前提”值得再三强调。很多初学者随手对一个无序数组写二分,结果自然是错的。这也体现出数据结构课程反复在讲的一件事:数据的组织方式,决定了算法能否高效运行。

4.2 排序算法对比:为什么不能只背代码

排序在“数据结构1”里占据相当大的比重,各种算法不下十种,而且期末必考、考研必考、面试也常考。很多人的策略是背代码,考完就忘。我的观点是:排序算法的价值不在于背,而在于理解每种算法背后的“基本思想”。

冒泡排序、选择排序、插入排序属于简单排序,平均时间复杂度都是O(n²)。它们更适合作为入门理解。比如插入排序就像打扑克时整理手牌,每来一张牌,就插入到前面已经有序的牌堆中。这个画面一旦建立,你写代码时就会很顺畅。

进阶一点的希尔排序、归并排序、快速排序,就体现“分治”思想了。快速排序的核心是选一个基准(pivot),把数组分成小于基准和大于基准的两部分,再递归排序。它的平均复杂度是O(n log n),但最坏情况能达到O(n²),这就是为什么有些题目会专门考察“快排在最坏情况下的复杂度”。归并排序更稳定,任何情况下都是O(n log n),但需要O(n)的额外空间。

堆排序则借助完全二叉树(堆)的结构,先建堆再不断取出堆顶元素,复杂度稳定在O(n log n),而且是原地排序,不需要额外空间。这部分就是“栈和队列”章节铺垫过的“树结构”知识在排序中的应用。各种排序算法的稳定性也是一个高频考点:不稳定排序的代表有选择排序、快速排序、堆排序、希尔排序;稳定排序的代表有冒泡、插入、归并。理解稳定性的关键是“相等元素的相对顺序是否会被改变”,别死记,拿一个具体例子走一遍就记住了。

在选型上,实际开发时通常不会自己写排序算法,而是直接调用语言的排序库,比如C的qsort、Python的sorted、Java的Arrays.sort。但为什么还要学?因为你要能判断“什么时候用稳定排序”“什么时候数据量太大必须外部排序”“什么时候用计数排序能到O(n+k)”。这些判断能力,才是面试官想看到的。

4.3 复杂度分析:面试官真正在问什么

数据结构这门课一开始就会讲算法复杂度,但很多人学完一整章“线性表”就把复杂度忘了。实际上,复杂度分析是贯穿整个学习和考核的主线。大O记号表达的是“算法运行时间随输入规模增长的量级”,它不关心常数系数,只关心增长趋势。O(1)表示常数时间,O(log n)增长很慢,O(n)线性增长,O(n²)平方级增长,O(2ⁿ)指数爆炸。

在分析复杂度时,最常用的方法是“看循环”:单层循环遍历n个元素通常是O(n),双层嵌套循环通常是O(n²),每次规模减半的循环通常是O(log n)。但更准确的做法是计算“基本操作执行次数”,然后取最高阶项去掉系数。

我见过太多人在分析递归算法复杂度时犯难,比如斐波那契数列的朴素递归。这里有个很有用的分析角度:画出递归调用树。朴素斐波那契递归的调用树近似一棵满二叉树,节点数随层数指数增长,所以时间是O(2ⁿ);而带记忆化(memoization)的递归,每个子问题只算一遍,时间降到O(n)。这就是“为什么有时候加一个数组缓存,性能就能提升几个数量级”的直观解释。

数据结构和算法的关系,在复杂度分析这里体现得最清楚:数据结构的优劣,必须用算法复杂度来衡量;算法的效率,也必须建立在合适的数据结构之上。你后期刷题、准备面试、做项目,所有性能优化的讨论,几乎都要回到复杂度这个“度量衡”上。

5. 实验报告、期末复习与考研备考的实操路线

5.1 一份能拿高分的实验报告是怎么写的

说完了知识体系,再来聊聊更贴近日常的场景。学数据结构,高校里流行一种东西叫数据结构实验报告。很多同学觉得写报告是“形式主义”,干脆从网上复制模板,结果实验课分数不高,还什么都没学到。以我的经验,实验报告是梳理知识的最佳工具,认真写一份报告,比闷头刷三道题还有效。

一份高分实验报告,通常包含五个部分:实验目的、实验内容与要求、设计与实现(核心)、测试结果与分析、实验总结。最关键的是“设计与实现”这一步,不要只贴代码,要先用文字和插图解释数据结构的组织方式。比如实现一个基于链表的通讯录管理系统,你应该先定义节点结构体,说明每个字段的意义;再画出“头节点插入”“删除节点”的示意图;最后才给出代码。这种“先想后写”的方式,能强迫你把“结构设计”想清楚。

测试部分也要认真写。不要只放一张“运行成功”的截图,而要列出一个测试用例表:输入什么数据、预期输出什么、实际输出什么、是否符合预期。尤其要测试边界情况,比如删除空链表中的节点、在满队列中入队、查找不存在的元素。老师看到这种报告,第一反应就是“这位同学真的动手了”。

实验报告本身也是面试时可以展示的项目素材。我认识一位校招拿到不错offer的同学,他简历上的项目栏没有高大上的系统,而是写了一个“基于哈希表的校园卡查询系统”,实验报告里的设计思路、时间复杂度和优化记录,都成了他在面试中讲项目的素材。简而言之,别把实验报告当作业,把它当成你的第一个“项目文档”来写。

5.2 期末复习三步走:习题集怎么刷最有效

期末复习最忌讳的是“从头翻课本”,因为内容太多,几天根本翻不完。更高效的策略是“三步走”:梳理知识框架、锁定薄弱点、刷真题。

第一步,画知识框架图。不需要很细,但要把“线性表→栈队列→串→树→图→查找→排序”这条主线理清楚。每个章节至少能说出三个问题:核心数据结构是什么?典型操作的时间复杂度是多少?有哪些经典应用?这一步大概花半天时间就能完成。

第二步,做一份往年真题或期末模拟卷,掐时间做。做完之后不要只对答案,而是把错题对应的知识点标出来,比如“二叉树的层次遍历实现错了”“堆排序调整过程画漏了一步”。这些错题反映的就是你的薄弱点,如果发现排序多道题都错,那就说明排序算法这个模块需要专门回炉。

第三步,针对薄弱点刷习题集。市面上的习题集很多,没必要全做,重点是重复练习薄弱题型。比如折半查找例题、AVL树旋转、图遍历序列,这些题型高度固定,属于“会就是会,不会就是不会”的类型。建议每类题刷到“看到题目就能条件反射”的程度,也就是说,模拟题考试时不能在推导上花太多时间。

这里特别提醒一点:不要忽略“手算”练习。数据结构的期末试卷有很多需要手写推导的题,比如画出插入关键字后的二叉排序树、模拟快排的一趟划分过程、计算哈希表的平均查找长度。这些在电脑上敲代码反而练不到手感,必须拿纸笔一步步画。我见过不少代码写得很溜的同学,一遇到画图题就卡壳,就是因为平时代码写多了,手动模拟练少了。

5.3 考研数据结构的备考节奏建议

如果你在准备考研408,数据结构考试覆盖的内容会比普通期末考试更宽更深。结合近几年考研数据分析,数据结构部分最常出现的题型包括:时间复杂度分析、二叉树遍历与性质、图的应用(最小生成树和最短路径)、折半查找相关计算、各种排序算法的过程模拟和复杂度比较。

备考节奏上,我的建议是“三轮复习”。第一轮以教材和基础网课为主,目标是“听懂 + 看懂代码”,这一轮不要追求速成,遇到不懂的图、树操作,多花几天把它画熟;第二轮以刷题为主,拿一本条件比较好的习题集,分章节专项突破,同时开始做历年真题;第三轮是冲刺阶段,成套做真题,掐时间模拟,重点背“概念性考点”和“结论性知识”,比如各排序算法的最好、最坏、平均情况复杂度表。

有一个很多考生都会踩的坑是:只看视频不写代码。408数据结构虽然没有机试,但考试中会出现类似“写出删除单链表中所有值为x的节点的算法”这种代码题,你不亲手写,根本不知道自己会在边界条件上错多少。哪怕初试是笔试,也建议每周在电脑上敲几道基础算法题,保持代码手感。另外,“从小到大到大范围的真题”这个顺序很重要,很多人一开始就做整套真题,错很多,心态崩了,反而影响复习节奏。

6. 从数据结构1到数据结构2:进阶方向与避坑清单

6.1 常见错误与调试心得

这部分我想重点说一些我在学习和带教过程中反复见到的“经典翻车现场”。这些坑非常具体,却很少被教科书写出来。

第一个坑是数组越界。C语言不像Python会自动检查下标范围,数组越界时程序可能不会立刻崩溃,而是在未来某一次操作时突然出错,这种错误极难排查。一个典型场景是循环队列的实现,很多人会把rear和front的计算搞混,导致入队时覆盖了未出队的元素。建议在写这类代码时,每一处涉及下标的地方都写清楚注释,并专门对边界情况做测试。

第二个坑是链表的“指针丢失”。比如删除单链表节点时,如果你先释放了当前节点,而没有提前保存下一个节点的地址,那程序下一步就找不到链表了。这个问题的本质是对“指针指向”和“内存释放”的顺序没想清楚。我的调试心得是:画图。在纸上画一个链表,标出当前指针p、前驱指针prev和要删除的节点,一步步改写箭头,代码自然就对了。

第三个坑是递归的终止条件不完整。很多初学者写二叉树的高度,只写if (node == NULL) return 0;,却忘了对只有单子树的节点做处理。虽然运行起来可能不报错,但在某些特殊输入下结果就是错的。我的建议是:每个递归函数,动手之前先想清楚“最小情况是什么”。对于二叉树,最小情况通常是空节点;对于链表翻转,最小情况是只有一个节点或者空链表。

第四个坑是完全不看复杂度。有些同学用Python刷题,跑通了就觉得完事了,但一提交就超时。尤其在做“括号匹配”“队列模拟”这类题目时,如果底层用了list.pop(0)这种头部删除操作,复杂度是O(n),数据量一大就必超时。建议写完代码之后,自觉分析一次复杂度,如果发现某一步有O(n²),就该想一想能不能用栈、队列或者哈希表把它降下来。

6.2 往哪走:从基础结构到高级主题

学完“数据结构1”,也就是掌握了线性表、栈与队列、字符串、二叉树、图的基础遍历、查找和排序,你已经打下了很扎实的底子。很多人问我要不要马上学高级结构,比如红黑树、B树、并查集、堆的高级应用,我的回答是:先把基础操作练熟,再往上走。

所谓练熟,标准其实不高:能在一小时内无参考地写出单链表的创建、插入、删除、反转;能在纸上画出AVL树四种旋转;能徒手写出快排、归并排序、堆排序的代码;能讲清楚BFS和DFS的区别并写出模板。达到这个水平,数据结构1阶段就算真正学透了。

之后可以往几个方向扩展。如果对工程应用感兴趣,可以深入了解哈希表的冲突处理(链地址法、开放定址法)和一致性哈希,这些在缓存、分布式系统里非常常见;如果对系统底层感兴趣,可以钻研B+树和LSM树,这是数据库引擎的核心;如果目标是竞赛和刷题,学一学并查集、线段树、树状数组这些进阶数据结构,它们会在很多高难度题目中派上用场。

这里我还想多说一句关于VBA高级数据结构的事。有些人可能在Excel里用VBA处理数据,会觉得“VBA也能讲数据结构”?答案是当然可以。VBA里的Dictionary就是哈希表的实现,Collection可以当作动态数组用,用数组模拟栈和队列也非常方便。很多人用VBA处理表格数据时,删除一行就遍历后逐行删除,结果速度慢得吓人,如果知道“从最后一行往前删”或者“先在内存中筛选再一次性写入”,效率能提升几十倍。这虽然不是正统数据结构课程的内容,但它恰恰证明了数据结构思想在任何语言、任何场景下都在发挥作用。

6.3 我踩过的坑和后来才明白的事

写到这里,我想分享几个自己当年没想明白、后来才体会到的事。

第一件事是“数据结构一定要自己动手画图”。我读本科的时候,觉得链表、二叉树这些不就是几行代码吗?所以很少画图。直到准备考研时才发现,很多题目不是靠理解,而是靠“脑子里有一幅动态图”才能做对。比如红黑树的旋转,我看视频看了三遍都记不住,后来自己在纸上画了二十几个节点的插入和删除过程,一下就通了。现在带新人,我第一句话往往就是:拿支笔。

第二件事是“代码和理论要交替进行”。有些人热衷于看书,把时间复杂度的推导过程研究得特别深,但一写代码就卡壳;有些人正好相反,代码写得很顺,但问他为什么用队列而不是栈,却说不出所以然。正确的做法是:读一个知识点,立刻上机验证;跑通一个算法,再回来推导复杂度。书和代码,一只脚在岸上,一只脚在水里,交替前进才不容易累。

第三件事是“别怕慢,怕的是不敢写长代码”。数据结构1的代码量,其实也就是几页纸,但很多人一看链表插入删除的代码,觉得长就产生了畏难情绪。我见过最快的学生,不是天赋型选手,而是那种“一行一行敲、错了就调,坚决不看答案”的人。慢是正常的,在调试中把每一个错误都弄懂,才是真正的学习。抄代码一时爽,考试火葬场,这话虽然糙,但道理是真切。

如果这篇文章能帮你理清“数据结构1”该怎么学、该往哪用力,那就算没白写。数据结构的门槛不在智商,而在投入的方法和耐心。把那些看似零散的知识点串成一张网,你会发现,后面的路会越走越宽。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 9:49:59

PHP网约车H5系统源码:全链路生产级实现与高并发抢单设计

简介&#xff1a;这是一套基于Yii框架开发的PHP网约车H5系统源码&#xff0c;面向Web全栈开发者与PHP中级学习者&#xff0c;提供乘客端、司机端及后台管理三端一体化解决方案&#xff0c;适用于毕业设计、创业原型验证或本地化打车平台二次开发。资源包共2000个文件&#xff0…

作者头像 李华
网站建设 2026/10/10 9:49:27

阿里云ECS上用Docker部署HiClaw的完整指南

1. 部署前的整体思路与方案选型1.1 为什么选阿里云服务器跑HiClaw先说结论&#xff1a;如果你只是想快速验证HiClaw这套服务能不能跑通、要不要长期挂机运行&#xff0c;阿里云ECS是目前上手成本最低的选择之一。我前后在好几台机器上折腾过HiClaw的部署&#xff0c;包括本地虚…

作者头像 李华
网站建设 2026/10/10 9:49:25

Windows下OpenClaw部署实操:本地模型接入与Skill配置

最近我在自己的Windows主力机上折腾OpenClaw&#xff0c;前后断断续续弄了小半天&#xff0c;中间还翻了一次不小的车。这工具最近讨论度确实高&#xff0c;但多数教程要么停在“接云端API跑个Demo”&#xff0c;要么只讲Linux/macOS怎么操作&#xff0c;真正把Windows下从零装…

作者头像 李华
网站建设 2026/10/10 9:49:12

Java的注解:代码的元数据

378 Java的注解:代码的元数据 你可能已经写过@Override,但你知道它到底是什么吗?注解(Annotation)是Java 5引入的一种"元数据"——它是贴在代码上的"标签",告诉编译器或框架"这段代码有什么特殊含义"。 一、什么是注解? 注解就像代码的…

作者头像 李华
网站建设 2026/10/10 9:47:33

Winutils配置全解析:解决Hadoop在Windows本地开发环境搭建难题

简介&#xff1a;在Windows系统上部署Hadoop集群时&#xff0c;winutils.exe是开发与运维人员绕不开的关键适配组件。它弥补了Hadoop对Unix/POSIX特性的依赖&#xff0c;解决了Windows下命令行支持、HDFS操作、Kerberos安全认证及环境变量配置等核心短板。压缩包内含189个文件&…

作者头像 李华
网站建设 2026/10/10 9:47:31

PS5扩容与优化实战:M.2 SSD加装、散热摆位与系统设置全攻略

最近接手了一台 PS5 的折腾任务&#xff0c;起因是朋友抱怨硬盘空间老是不够用&#xff0c;下载一个大作要反复删游戏。我拿到手之后&#xff0c;本着“任何一台 PS5&#xff08;AnyPS5&#xff09;都能变得更适合自己的使用习惯”的思路&#xff0c;从硬件扩容、系统设置到日常…

作者头像 李华