☰
数据结构学习笔记:从线性表到图排序的考点梳理与记忆方法
2026/9/30 3:04:22 网站建设 项目流程

作为在数据结构这门课上从"背了忘、忘了背"到最终形成自己知识框架的过来人,我深知一份好的笔记对学习有多重要。市面上教材很多,严蔚敏老师的C语言版、李春葆老师的教材都是经典,但对于初学者来说,最常遇到的问题不是没有资料,而是资料太多太散,学完一章忘了前一章。这篇笔记整理的思路,不是我一个人闭门造车的产物,而是结合了408考研大纲、期末复习重点和实际编码经验反复打磨出来的。如果你正在准备期末考试、考研408,或者单纯想把这门硬课学扎实,这篇文章应该能给你一个清晰的地图。

我整理笔记有个原则:不抄书,只做知识点的"二次加工"。也就是说,笔记记录的必须是经过自己理解、重新表述、并且标注了"为什么这样设计"的内容。单纯把教材目录搬到笔记软件里没有任何意义,那只是换个地方存了一遍原文。真正有价值的笔记,是当你翻到某一页时,能立刻想起来当时卡在哪个点上、用了什么类比才想通、写代码踩过什么坑。

1. 数据结构的宏观认知:为什么这门课既要"背"又要"想"

很多人学数据结构最大的误区,就是把这门课当文科背。链表有几种、二叉树遍历有几种、排序算法时间复杂度表背得滚瓜烂熟,但一到手写代码就懵。另一部分人则相反,觉得一切都要从零推导,连红黑树旋转都要当场证明一遍,效率极低。我的经验是,数据结构这门课有一个"背与想"的平衡点。

1.1 那些必须"背"下来的内容

时间复杂度和空间复杂度分析是必须形成肌肉记忆的。不是说你要背下来某个算法是O(n)还是O(log n),而是看到代码结构,脑子里能立刻反射出复杂度量级。比如看到while循环里变量每次乘2,立刻想到O(log n);看到双重循环嵌套且都是线性增长,立刻想到O(n²)。这个能力考场上不可能临时推导,必须在平时刷题时反复强化。

常见的复杂度排序必须滚瓜烂熟:O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)。我自己的记忆技巧是把它和实际生活场景对应:O(1)是翻一本字典的某一页,O(log n)是二分查找电话簿,O(n)是逐页翻书找一句话,O(n log n)是整理一堆扑克牌,O(n²)是两两比较所有人的生日。

1.2 那些必须"想"明白的内容

为什么数组的插入是O(n)而链表的插入是O(1)?为什么快排最坏情况退化到O(n²)?为什么哈希表的冲突解决方式会影响查找性能?这些问题靠背是背不出来的,必须理解数据结构的底层存储方式和访问逻辑。

我用一个生活化类比来理解数组和链表的本质区别:数组就像电影院里的固定座位,每个人座位号连续,你想在第5排和第6排之间加一个人,所有人都得往后挪一个位置——这就是插入O(n)的由来。链表就像游乐园里排队的游客,每个人都只记得下一个人是谁,想插队只需要让前面的人"记住"你,你再"记住"后面的人——这就是插入O(1)的由来。但代价是链表想找第10个人,只能从队头一个一个数过去,数组却可以直接算出第10个座位在哪。

笔记里专门有一页记录这些"本质区别",用表格做对比:

对比项数组链表
存储方式连续内存分散内存+指针连接
随机访问O(1),直接计算地址O(n),必须遍历
插入/删除O(n),需要移动元素O(1),只需修改指针
空间开销无需额外指针空间每个节点需要额外指针
缓存友好性高,局部性原理低,节点可能分散

这张表看起来简单,但我在上面花了很多功夫才真正理解"缓存友好性"是什么意思。后来学了计算机组成原理才明白,数组的连续内存让CPU缓存命中率更高,而链表的节点在堆里到处乱跳,每次访问都可能缓存失效。数据结构的学习确实需要这样横向打通知识点。

2. 线性表、栈与队列:从"结构"到"工具"的思维转变

线性表是数据结构的地基,栈和队列则是地基上最常用的两种"工具"。很多初学者学完这一章觉得内容少,无非是链表数组的增删改查,但学完后面对括号匹配、表达式求值、循环队列判满判空时又看不懂代码。问题出在:你没有把栈和队列从"存储结构"上升为"解决问题的手段"。

2.1 链表:头节点到底加不加?

链表这块,很多教材一开始就抛出一个让初学者困惑的问题:单链表要不要头节点?严蔚敏老师的C语言版本里,几乎所有链表操作都带头节点,让"第一个节点"的操作逻辑和后面的节点统一起来。但如果你自己写代码,可能会发现不带头节点也写得通。

我个人的建议是:做题和考试时采用带头节点的写法。原因不只是教材导向,更是因为它让代码逻辑更统一。带头节点后,空表判断从 head == NULL 变成 head->next == NULL,插入和删除第一个实际节点时不用单独修改头指针,代码分支更少,不容易出错。

笔记里我把带头节点和每头节点的差异整理成了对照:

  • 带头节点:头指针指向固定的头节点,头节点数据域不存有效数据。空表判断 head->next == NULL。
  • 不带头节点:头指针直接指向第一个有效节点。空表判断 head == NULL,插入删除首节点时要修改头指针本身,需要传入二级指针或返回新头指针。

实际做题时,这两种写法在LeetCode或其他OJ平台看到的题解里经常混用,但如果你能熟练掌握带头节点的写法,绝大多数链表题都能顺下来。真正容易踩坑的是双链表的插入和删除顺序:很多初学者写双链表插入时,先改前驱指针发现节点"断链"了。我总结了一个铁律——双链表插入先搞定"新节点的前后连接",再断开原来的连接。

2.2 栈的应用:那几道"用栈实现"的经典题

栈这块,我觉得最有价值的不是栈本身的代码实现(太简单了),而是它的三个经典应用场景:括号匹配、表达式求值、函数调用栈的模拟。

括号匹配是入门体验栈"后进先出"特性的第一道坎。逻辑说起来很简单:遇到左括号就入栈,遇到右括号就弹出栈顶元素检查匹不匹配。但实际写代码时,有个细节特别容易忽略——遍历完整表达式后,栈必须是空的才算合法。比如"(()"这个情况,左括号永远等不到右括号,如果你只看配对过程,会发现每一步都"匹配成功",但最后栈里还剩一个左括号。这个细节我在笔记里用红色标注了,因为期末考场和笔试中,这类边界条件就是区分度所在。

表达式求值分为中缀转后缀和后缀表达式求值两步。中缀转后缀的规则,教材上写了一大段,但我整理成了4句话规律:

  1. 操作数直接输出。
  2. 栈顶运算符优先级低于当前运算符时,当前运算符入栈;否则弹出栈顶运算符输出。
  3. 遇到左括号直接入栈,遇到右括号弹出栈内元素直到左括号(左括号不输出)。
  4. 扫描结束后,依次弹出栈中剩余运算符。

如果你在看这部分笔记时手边有纸笔,强烈建议手动模拟几遍。尤其是"中缀转后缀"这个操作,刷20个表达式的模拟量比看10遍教材都有用。

2.3 队列:循环队列的"浪费一个空间"设计

循环队列是线性结构章节最容易被扣分的地方。很多初学者困惑:为什么循环队列判空是 front == rear,判满是 (rear + 1) % MAXSIZE == front,它们看起来是一模一样的关系啊?

这个问题的根源在于:如果允许队列满时 rear == front,那和"队列空"就无法区分了。所以标准的循环队列设计,人为牺牲一个存储单元,让"队满"的状态变成 rear 恰好走到 front 的前一个位置。换句话说,队列实际能存放的元素个数是 MAXSIZE - 1。

从"为什么"角度理解这个设计,比记住公式强得多。我在笔记里画了循环队列的入队和出队方向示意图,并在旁边写了一段体会:循环队列本质上是用取模运算把线性数组"卷"成一个环,取模操作是理解所有循环结构的关键。front 指向队首元素,rear 指向队尾元素的下一个位置,入队时元素放进 rear 位置然后 rear = (rear + 1) % MAXSIZE,出队时取出 front 元素然后 front = (front + 1) % MAXSIZE,这两个操作模式高度对称,写代码时不容易记混。

双端队列这个关键词在热搜里很靠前,说明很多人也在找它的笔记。双端队列其实就是允许两端都能插入和删除的队列,输入受限的双端队列只允许一端插入,输出受限的双端队列只允许一端删除。它最直观的应用场景是"窗口滑动"类问题,比如在固定长度的滑动窗口里维护最大或最小值,用双端队列能做到O(n)的线性复杂度,这个思路是LeetCode上很多hard题的解法基础。

3. 树与二叉树:递归思维的分水岭

树这一章,是数据结构学习路上第一个真正的分水岭。前四章学线性结构,思维方式还是"遍历+标记",到树这里突然要求你学会"递归思维"——一个节点的问题可以拆成左右子树的问题,层层嵌套,最后归约到空节点这个基本情况。很多同学就是从二叉树开始发现这门课"变难了"。

3.1 二叉树的遍历:先序、中序、后序到底在干什么

遍历这件事,光看伪代码很容易产生"我懂了"的错觉。先序遍历:先访问根,再遍历左子树,再遍历右子树——三句话而已。但真让你画一个递归过程,很多人会发现自己的"递归栈"在脑子里转不过来。

我的经验是,遍历问题的核心在于理解程序调用栈的"深入与回归"。每次递归调用都会把当前函数的状态压入调用栈,等子树递归完成后,再弹栈恢复现场,继续往下执行。我笔记里专门用一个三层二叉树的例子,手写了每一步递归调用时栈的状态变化。整个过程写下来,从根节点A出发,递归左子树B,B再递归左子树D,D是叶子节点返回,然后B递归右子树E……当你能完整画出这个调用栈的进出过程,二叉树遍历就再也不是背代码了,而是真正理解了递归的底层逻辑。

关于三种遍历的关系,还有一个非常实用的结论:已知中序+先序可以唯一确定一棵二叉树,已知中序+后序也可以,但只知道先序+后序不行。原因是先序(或后序)能确定根的位置,而中序能根据根把左右子树的范围划分开,两者配合才能确定唯一的树结构。这个考点在期末和考研里几乎是必出的,务必理解而不是硬背。

3.2 树的存储结构:双亲、孩子、兄弟,为什么最终选了孩子兄弟法

树的存储结构在教材上讲了三种:双亲表示法、孩子表示法、孩子兄弟表示法。很多同学学到这里觉得繁琐,认为只要会用就行。但如果你去看408考研的历年考题,这块的出题频率比你想象的高得多。因为它考的不是背书,而是你对"逻辑结构如何映射到存储结构"的理解能力。

三种方式的核心区别我整理成了一段话:

  • 双亲表示法:只记录每个节点的父节点下标,找父节点是O(1),但找孩子要遍历整个数组,适合"认祖归宗"类操作。
  • 孩子表示法:每个节点维护一个孩子链表,找孩子快但找父节点难,而且链表操作多。
  • 孩子兄弟表示法:把一棵普通的树,用"每个节点只记录第一个孩子和下一个兄弟"的方式转换成二叉树存储。这样可以把树的问题统一归约到二叉树问题,复用二叉树的所有成熟算法。

孩子兄弟法是最优雅的方案,它彻底打通了"树"和"二叉树"两个世界。很多时候你看到一个二叉树算法的应用场景很困惑,比如怎么用二叉树存储森林,背后的原理就是孩子兄弟法。我把这个方法在笔记里单独开了一节,配了转换示意图,从爷爷辈的一棵三叉树转换到兄弟链后,你能够看到每个节点的"左指针"指向第一个孩子,"右指针"指向下一个兄弟,树的层数信息被压缩进指针结构里。

3.3 二叉排序树与平衡因子:为什么旋转是必要的

二叉排序树BST的定义很简单:左子树所有节点值小于根,右子树所有节点值大于根。但它的性能完全取决于树的形状,一棵极度倾斜的BST可能退化成一个链表,查找复杂度从O(log n)变成O(n)。

AVL树(平衡二叉树)的引入就是为了解决这个退化问题,它的核心机制是平衡因子——左子树高度减右子树高度,绝对值不能超过1。一旦插入或删除节点导致某个节点平衡因子绝对值超过1,就要通过旋转操作恢复平衡。四种旋转:LL型右旋、RR型左旋、LR型先左后右、RL型先右后左。

笔记里我把自己摸索出来的记忆方法写了下来:LL型只看"三个节点一直偏左"的形态,解决办法是找到中间那个节点提起来当根,另外两个挂到两边。不一定非要记忆每个指针怎么改,而是从"哪个节点失衡""失衡形态是连续向左还是连续向右"出发去判断旋转类型。多画几次旋转示意图后,你会形成一种"手感",做题时不用反复推导。

3.4 哈夫曼树:从"带权路径长度最小"理解编码本质

哈夫曼树这一节,如果只记构造方法就太亏了,它的应用场景——哈夫曼编码——是信息论里一个绝美的应用。给定一组字符及出现频率,用哈夫曼树构造出每个字符的二进制编码,要求整体编码长度最短,而且每个字符的编码不能是另一个字符编码的前缀。

构造过程一句话概括:每次从森林里选两个权值最小的树合并,新根权值为二者之和,放回森林,重复直到只剩一棵树。这个过程用优先队列(最小堆)实现非常自然,每次取两个最小元素,合并后push回去,复杂度是O(n log n)。

哈夫曼编码"前缀编码"的特性,是因为每个字符都在叶子节点上,没有任何字符的编码路径会经过另一个字符的编码。如果某个字符是另一个字符的祖先节点,那它的编码就会成为另一个的前缀,解码时会产生歧义。保证字符对应叶子节点,就是哈夫曼编码正确性的根基。

4. 图论算法:从"遍历"到"最短路径"的思维升级

图是数据结构里概念最多、算法最密集的一章。邻接矩阵、邻接表、十字链表、邻接多重表、DFS、BFS、Prim、Kruskal、Dijkstra、Floyd、拓扑排序、关键路径……一个学期最后几周的高强度内容全在这一章。如果前面的树学得扎实,图其实可以看作"树的一般化"——树是只有一条路径连接的图,图是多对多的关系网络。

4.1 存储结构的选择:邻接矩阵和邻接表怎么权衡

邻接矩阵是二维数组存储顶点间关系,判断两个顶点是否相邻是O(1),但存储空间是O(V²),对稀疏图非常浪费。邻接表为每个顶点挂一个链表,存储空间是O(V+E),但判断两个顶点是否相邻需要顺着链表找,最坏O(V)。

选哪个?我的经验总结成一句话:稠密图用矩阵,稀疏图用表。但在实际笔试中,题目通常会给你一个具体的图让你画出存储结构,这时候你需要熟练掌握两种表示法的绘图规范。尤其是邻接表,每个顶点后的链表节点顺序,如果题目没有说明,一般按输入顺序或顶点编号递增顺序排列即可,但如果题目明确说了"按某种顺序",一定要严格遵守。

4.2 最小生成树:Prim与Kruskal的核心差异

最小生成树算法有两个经典实现:Prim和Kruskal。

Prim算法的思路是"从一个点开始扩张领地":初始选定一个顶点加入集合U,每次从连接U和V-U的边里挑一条权值最小的边,把新顶点并入U,重复直到所有顶点都在U里。这个过程中,U始终是一棵连通的树,所以每次选边时不会产生环。

Kruskal算法则是"全局选边":把所有边按权值排序,从小到大逐条加入,只要加入后不形成环就保留,直到选了V-1条边。Kruskal不会维护一个"当前连通区域",所以它的关键操作是判断一条边的两个端点是否已经连通——这个操作用并查集实现就是O(α(n)),非常高效。

我在笔记里专门对比过这两者选边时的差异:Prim是"点视角",适合稠密图(用邻接矩阵或堆优化);Kruskal是"边视角",适合稀疏图(边数少,排序成本可控)。考研里如果给一个具体的图让你求最小生成树,两种方法都要会手工模拟,并且能说明每一步选择的依据。

4.3 最短路径:Dijkstra不能有负权边,Floyd可以

最短路径算法是图论的"大魔王"章节。Dijkstra算法用贪心策略,每次从未确定最短路径的顶点里选出当前距离最小的,并松弛它的邻接边。Dijkstra的正确性依赖一个前提:所有边的权值非负。如果存在负权边,先被"确定"的顶点可能在后面被一条负权边"绕近",导致算法失效。

Floyd算法则完全不同,它动态规划地枚举所有"中间顶点",用三维循环(实际代码里通常压缩成二维滚动数组)更新任意两点间的最短路径。Floyd能处理负权边,但不能有负权回路。它的时间复杂度是O(V³),所以只适合顶点数不多的场景。

我犯过的错误和大多数人一样:一开始没搞清楚"松弛"操作到底在干什么。松弛的本质,就是检查"经过中间点k,会不会比直接走更短"。如果 dis[i][j] > dis[i][k] + dis[k][j],就更新 dis[i][j]。当你真正理解了"以k为中间点"这句话,Floyd的代码就只是一层固定格式的循环嵌套。

4.4 拓扑排序与关键路径:有向无环图的应用

拓扑排序是对有向无环图DAG的顶点的一种线性排列,要求每条边的起点都在终点之前。算法思路很朴素:每次找一个入度为0的顶点输出,然后删除它及其出边,更新剩余顶点的入度,重复。这个过程用队列或栈实现,如果最终输出的顶点数量小于总顶点数,说明图中存在环,拓扑排序失败。

在408考研中,拓扑排序经常和"判断一个有向图是否有环"结合出题,也经常和DFS的"递归栈标记"方法对比。两种方法各有适用场景:拓扑排序适合输出一个合法序列,而DFS三色标记法(白、灰、黑)可以顺便做环检测。

关键路径的问题是AOE网(边表示活动的有向无环图):边的权值表示活动持续时间,求从源点到汇点的最长路径,因为整个工程的完工时间取决于最长的那条路径,也就是"瓶颈路径"。求关键路径需要先算事件的最早开始时间和最迟开始时间,两者的差为0的事件组成关键路径。

5. 查找与排序:期末和考研的"兵家必争之地"

查找和排序是数据结构考试里最"细"的部分。二分查找的下标变化、哈希冲突处理、快排的划分过程、堆排序的调整过程,每一个都是高频考点。我见过太多同学顺序表、链表学得好好的,到了排序这里开始晕头转向,因为排序算法涉及大量的"手动模拟过程",一步错步步错。

5.1 折半查找:那些教材没明说的细节

折半查找(二分查找)的前提是顺序存储且有序。代码逻辑很简单,但有两个细节很多人没注意:

第一,中点取法。标准写法是 mid = (low + high) / 2,但更好的写法是 mid = low + (high - low) / 2,因为前一种写法在 low + high 特别大时可能整型溢出。这个细节在考研机试或面试手写代码时会被问到。

第二,查找判定树。折半查找的过程可以用一棵"二叉判定树"表示,树中每个节点代表一次中点的比较。这棵树的形态揭示了折半查找的时间复杂度O(log n)和平均查找长度。考研題经常给出判定树让你计算ASL(平均查找长度),或者反过来问你"对长度为n的有序表折半查找,最多比较几次"。

计算ASL是这一节的常见题型,我记了公式:成功时的ASL等于各层节点数乘以对应层次之和除以节点总数;失败时的ASL等于各外节点(空孩子位置)的层次之和除以失败可能的总数。这里如果不画判定树,几乎是算不对的,所以无论平时练习还是考场,都建议动手画出完整的判定树。

5.2 哈希表:冲突处理方式是期末必考

哈希表的考点集中在哈希函数设计和冲突处理。常见的冲突处理方法有开放定址法(线性探测、平方探测、再哈希法)和链地址法。

线性探测的问题在于容易产生"堆积",一旦某块区域填满了,后续冲突的键都会往后推移,形成"聚集区",导致查找变慢。平方探测通过步长的平方变化减少堆积,但有一个限制条件:装填因子不能太大,否则可能无法找到空位。链地址法最直观,每个哈希槽挂一个链表,冲突的元素直接链在后面,查找时遍历链表即可。

期末笔试最常考的操作是:给定一组关键字和哈希函数、表长、冲突处理方式,让你画出哈希表并计算成功/失败的平均查找长度。这类题没有任何捷径,只能老老实实地按顺序插入每个关键字,记录每个关键字比较的次数。我在笔记里用红笔标注了一个易错点:删除哈希表中的元素时,线性探测法不能直接物理删除,因为会切断后续元素的探测链,只能做"懒惰删除"标记。这个问题在应用题和概念题里经常出现。

5.3 五种排序必须熟练到"随手就能模拟"

排序算法是数据结构笔记里最"干货"的部分。期末和考研要求掌握的排序包括:插入排序(直接插入、希尔)、交换排序(冒泡、快速)、选择排序(简单选择、堆排序)、归并排序、基数排序。这里我不打算把每个算法都详细贴一遍,那是教材干的事,我更想分享的是怎么通过"对比"把它们一次性记住。

八大排序的核心区别,我用这张表高度概括:

排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性
直接插入O(n²)O(n²)O(1)稳定
希尔排序O(n^1.3~2)O(n²)O(1)不稳定
冒泡排序O(n²)O(n²)O(1)稳定
快速排序O(n log n)O(n²)O(log n)不稳定
简单选择O(n²)O(n²)O(1)不稳定
堆排序O(n log n)O(n log n)O(1)不稳定
归并排序O(n log n)O(n log n)O(n)稳定
基数排序O(d(n+r))O(d(n+r))O(r)稳定

记忆技巧:稳定的排序只有三个——直接插入、冒泡、归并。口诀是"插冒归",角标加一点也可以想到"插冒归基"如果基数排序也列进来。快排虽然平均最快,但最坏会退化到O(n²),触发条件是每次划分都极度不平衡(比如数组本身有序且每次选第一个元素作枢轴)。

5.4 快排和堆排复习时我踩过的坑

快速排序的"划分"过程是笔试手写模拟的重灾区。我犯过的错误是用两个指针从两端交替扫描时,方向搞反了或者移动条件写错。正确流程是:选一个枢轴(通常取第一个元素),从右往左找比枢轴小的元素,找到后填入左侧空位;再从左往右找比枢轴大的元素,找到后填入右侧空位;交替进行,直到左右指针相遇,把枢轴放进去。一次划分完成后,枢轴左边都小于等于它,右边都大于等于它。每趟划分之后枢轴元素就固定不动了,这个递归过程是"分而治之"的直观体现。

堆排序对我来说是另一个难点。建堆和堆调整的代码需要"下沉"操作:把当前节点和它的左右孩子比较,找到最大的那个(大顶堆),如果不满足堆性质就交换,然后继续下沉。笔试里经常要求对一个小顶堆插入一个元素后演示堆的调整过程,这需要你非常熟练地掌握"完全二叉树用数组存储时,节点i的左孩子是2i+1、右孩子是2i+2、父节点是(i-1)/2"这三个下标公式。

我自己的经验是,堆排序不要只看文字描述,必须自己在纸上画一个数组对应的完全二叉树,然后模拟"从最后一个非叶子节点开始向上逐个调整"的建堆过程。当你能独立完成一次建堆和三次调整不犯错,这部分才算真正过关。

6. 持续更新机制:如何让笔记真正成为"长期资产"

前面说了很多知识层面的整理思路,最后想聊聊笔记本身怎么维护。数据结构内容量大,很多同学学期初写得兴致勃勃,几周后就荒废了。我自己的笔记能持续更新到现在,靠的是一套不算复杂的"更新机制"。

6.1 为每个知识点打三种标签

我最初整理笔记时,所有内容平铺直叙,复习时根本抓不住重点。后来改成在每节开头打标签,效果立竿见影。标签分三类:"必考"、"易错"和"联系"。

"必考"标签标注那些历年真题、期末考试卷反复出现的知识点,比如二叉树遍历、Dijkstra算法、快排划分过程。"易错"标签标注那些自己曾经做错过的具体细节,比如循环队列判满条件、(rear+1)%MAXSIZE、拓扑排序只适用于DAG。"联系"标签标注两个看似无关的知识点之间的桥,比如"孩子兄弟表示法(树的存储)和二叉树之间是互转关系""用栈实现深度优先遍历、用队列实现广度优先遍历"。这三个标签让复习变成"带着优先级扫描"而不是"从头到尾读一遍"。

6.2 每次学完必须产出"三件套"

给自己定了个规矩,每学完一个新的数据结构,强制要求产出三样东西:

  1. 一张手工绘制的示意图(比如链表的指针变化、二叉树的递归遍历过程、最小生成树的选边过程)。
  2. 一段自己写的、能运行的代码(哪怕是书上的例题,也要手敲一遍,不能复制)。
  3. 一个"给别人讲"的段落(用大白话解释这个数据结构解决了什么问题,典型应用是什么)。

这三样东西做下来,比读十遍教材都有用。尤其是第三条,很多概念你觉得"懂了",但真让你讲出来,会发现自己组织语言都困难。我笔记里很多"类比"和"记忆技巧"都是在这个环节里琢磨出来的。

6.3 定期重构:笔记是"活"的

我的笔记更新频率大概是这样的:每学完一章,补充一次;每做完一套题,补充错题涉及的知识点;每个月底,花半小时通读之前的笔记,把发现的新联系补充进去。这个月底"重构"环节经常带来惊喜——当你学完图和排序之后回头看栈和队列,会发现很多之前没看到的联系。比如"递归就是用栈实现的""BFS为什么能用队列实现而DFS更适合用栈或递归"。这种跨章节的联系,只有在你对整个课程有了全局视野后才会浮现出来。

如果有人问我,数据结构这门课到底怎么学?我的回答是:把笔记当作一个可以反复迭代的存根,而不是一次性的成品。第一遍学习时笔记粗糙一点没关系,关键是每个知识点都留下"自己理解的痕迹"。后面每次复习、做题、写代码时发现有新的体会,就回去修改旧的段落,让笔记始终处于"接近当前认知水平"的状态。持续更新这四个字,从来不在于更新得多频繁,而在于每一次更新的方向都是朝向更深的理解。

这篇笔记会继续更新下去,下一批打算补充的内容包括:考研408真题里图论编程题的常见套路、B树和B+树的对比笔记、以及用Python的pandas库从工程角度理解数据结构中的"索引"到底是怎么用树和哈希实现加速查找的。希望这份笔记整理的思路,能给你的数据结构学习带来一些有用的参考。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询