
说起《数据结构》很多初学者最痛苦的事情不是“看不懂”而是“今天看得懂明天全忘了”。今天刚把线性表背下来后天看到二叉树又懵了等到学图的时候前面的知识基本还给了课本。这种状态我也经历过后来反复对照课程、教材和代码练习才慢慢把零散的知识串成一张完整的知识网。本文以哈工大《数据结构》全44讲为学习主线系统梳理线性表、树、图、查找与排序这五大核心模块并在每个模块中给出可以直接运行的 C 语言代码示例。无论你是正在准备期末复习、应对考研专业课还是准备面试算法题这篇文章都能作为一份“课程笔记 上机代码 复习清单”使用。1. 为什么数据结构值得系统学一遍1.1 数据结构到底是什么一句话解释数据结构是计算机存储、组织数据的方式。同样是存一组学生信息用数组存和用链表存在插入、删除、查找时的体验完全不同同样是做查找顺序查找和折半查找在数据量变大之后性能差距可能是十万八千里。更准确地讲数据结构研究的是三件事数据的逻辑结构数据元素之间存在什么关系比如线性关系、树形关系、图形关系。数据的存储结构逻辑结构在计算机内存中怎么表示常见有顺序存储、链式存储、索引存储、散列存储。数据的运算针对每种结构实现增、删、改、查以及排序、遍历等基本操作。很多同学学数据结构的时候只盯着“代码实现”忽略了逻辑结构和存储结构之间的区别导致考试时概念题丢分上机时又不知道何时该用哪种结构。实际上先判断逻辑结构再选择存储结构最后设计算法才是正确的思考顺序。1.2 哈工大《数据结构》44讲的知识地图这门公开课共44讲内容覆盖了数据结构课程的全部核心知识模块。按照学习顺序可以把44讲拆成下面这张知识地图模块核心内容关键数据结构/算法常见代码训练绪论数据结构基本概念、算法复杂度时间复杂度、空间复杂度分析循环、递归的复杂度线性表顺序表、链表单链表、双链表、循环链表插入、删除、反转、合并栈和队列受限的线性表顺序栈、链栈、循环队列表达式求值、括号匹配、层次遍历辅助结构串字符串匹配KMP 算法模式匹配、Next 数组树与二叉树树的存储、二叉树遍历二叉树、线索二叉树、哈夫曼树、并查集递归遍历、层次遍历、哈夫曼编码图图的存储与遍历邻接矩阵、邻接表、DFS、BFS最短路径、最小生成树、拓扑排序查找静态查找、动态查找、散列折半查找、二叉排序树、AVL、哈希表哈希冲突处理、BST 增删查排序内部排序算法插入、交换、选择、归并、基数排序快排、堆排、归并的代码实现可以看出44讲并不是把每个知识点平均用力而是按“线性结构 → 树形结构 → 图形结构 → 查找 → 排序”这条主线层层递进。学完后你最终要能回答“面对一个真实业务问题应该选哪种结构 哪种算法”这个综合问题。1.3 这门课适合哪些人学在校学生正在上数据结构课需要一份与课程同步的复习笔记和代码示例。考研党数据结构是计算机考研408的重点科目需要把代码题和概念题一起抓。求职者面试手写算法题时链表反转、二叉树遍历、快排都是高频题。自学开发者写过业务代码但没系统学过数据结构想补上计算机基础这块短板。如果你属于以上任意一类都可以按下面章节的顺序把44讲当成一份“学习地图”每学完一个模块就对照示例代码自己敲一遍。2. 线性表一切数据结构的地基2.1 顺序表与链表的区别线性表是 n 个数据元素的有限序列核心特点是元素之间有一对一的线性关系。根据存储方式不同线性表分为顺序表和链表。顺序表用一段地址连续的存储单元依次存储数据本质就是数组。优点是支持随机访问用下标取元素时间复杂度是 O(1)缺点是插入和删除需要移动大量元素且扩容不方便。链表用一组任意的存储单元存储数据每个节点包含数据域和指针域。优点是插入和删除只需修改指针不需要移动数据缺点是不能随机访问要查找某个节点只能从头遍历。在常见的严蔚敏《数据结构C语言版》教材中顺序表和链表基本占据了前几十讲的篇幅足以说明它的重要性。考试中经常出现的“顺序表倒置”“链表反转”“两个有序链表合并”问题本质上都是对这两种存储结构的操作训练。2.2 顺序表插入操作的 C 语言实现下面给出顺序表插入操作的核心代码。理解这段代码的关键在于从后往前移动元素如果从前往后移动后面的元素会被覆盖。// 文件路径seqlist.c #include stdio.h #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int length; } SeqList; // 在顺序表 list 的 pos 位置插入 value // pos 取值范围0 pos list-length int insertList(SeqList *list, int pos, int value) { if (list-length MAX_SIZE) { printf(表已满无法插入\n); return 0; } if (pos 0 || pos list-length) { printf(插入位置非法\n); return 0; } // 从最后一个元素开始依次向后移动一位 for (int i list-length; i pos; i--) { list-data[i] list-data[i - 1]; } list-data[pos] value; list-length; return 1; } int main() { SeqList list {{1, 2, 3, 4, 5}, 5}; if (insertList(list, 2, 99)) { for (int i 0; i list.length; i) { printf(%d , list.data[i]); } printf(\n); } return 0; }运行后输出1 2 99 3 4 5时间复杂度分析顺序表插入操作平均需要移动 n/2 个元素所以时间复杂度是O(n)。这也解释了为什么频繁插入删除的场景不适合用顺序表。2.3 链表反转的 C 语言实现链表反转是面试和考试中最高频的链表题之一。常见思路是迭代三指针法用 prev、current、next 三个指针配合逐个把当前节点的 next 指向前一个节点。// 文件路径linkedlist.c #include stdio.h #include stdlib.h typedef struct Node { int data; struct Node *next; } Node; // 创建新节点 Node* createNode(int data) { Node *node (Node*)malloc(sizeof(Node)); node-data data; node-next NULL; return node; } // 反转链表 Node* reverseList(Node *head) { Node *prev NULL; Node *current head; Node *next NULL; while (current ! NULL) { next current-next; // 先保存下一个节点 current-next prev; // 翻转指针 prev current; // prev 前移 current next; // current 前移 } return prev; // prev 就是新头节点 } // 打印链表 void printList(Node *head) { Node *p head; while (p ! NULL) { printf(%d , p-data); p p-next; } printf(\n); } int main() { Node *head createNode(1); head-next createNode(2); head-next-next createNode(3); head-next-next-next createNode(4); printf(原链表); printList(head); head reverseList(head); printf(反转后); printList(head); return 0; }运行后输出原链表1 2 3 4 反转后4 3 2 1这里必须提醒一个新手极易犯的错误在修改 current-next 之前一定要先保存 next 节点。如果先执行current-next prev原来的后续节点就找不到了链表会断掉。2.4 线性表模块学习建议学完线性表后建议自己完成以下小任务来检验掌握程度用顺序表和链表分别实现插入、删除、查找操作。实现两个有序链表的合并。实现单链表的就地逆置不要用数组辅助存储。分析顺序表与链表在不同操作下的时间复杂度。很多同学觉得线性表简单直接跳过代码练习结果学到树和图时发现自己连指针都用不熟练这就是基础没打牢。线性表是后面所有结构的基础尤其是链表指针操作必须多练。3. 栈与队列两种特殊的线性表3.1 栈的特点与应用场景栈是只允许在一端进行插入和删除操作的线性表遵循“后进先出LIFO”规则。栈的插入叫入栈push删除叫出栈pop。如果把顺序表和链表比作一条自由进出的队伍那么栈就像一个只能从顶部取放的箱子。栈的经典应用包括函数调用递归调用时需要保存每一层函数的局部变量和返回地址用的是系统栈。括号匹配编译器检查{ [ ( ) ] }是否匹配就用栈来存储左括号。表达式求值将中缀表达式转换为后缀表达式再用栈计算结果。浏览器后退页面访问记录存入栈中点后退时从栈顶弹出上一页。下面是括号匹配的简单思路// 遍历字符串 // 遇到左括号 ( [ { 就入栈 // 遇到右括号 ) ] } 就出栈并检查是否匹配 // 如果最终栈为空且中途无不匹配说明括号匹配这种思路在考研选择题和面试手写题中都很常见核心是理解栈的“后进先出”特性。3.2 队列的应用场景队列是只允许在一端插入、另一端删除的线性表遵循“先进先出FIFO”规则。队尾插入队头删除。队列的典型应用场景操作系统中的任务调度多个进程排队等待 CPU。打印机任务队列先提交的任务先打印。二叉树层次遍历时需要用队列保存每一层的节点。图的BFS广度优先搜索借助队列逐层扩展。3.3 循环队列的判空与判满用数组实现队列时为了避免“假溢出”通常把数组首尾相接形成一个循环队列。循环队列常见的实现方式是初始化front rear 0入队rear (rear 1) % MAX_SIZE出队front (front 1) % MAX_SIZE判空front rear判满(rear 1) % MAX_SIZE front这里需要注意为了区分队空和队满循环队列会故意浪费一个存储空间。如果不浪费空间就无法通过 front 和 rear 的关系区分“空”和“满”两种状态。这也是考试中非常喜欢考察的一个细节。4. 树与二叉树递归思想的训练场4.1 树的基本概念树是 n 个节点的有限集合它有一个根节点其余节点可以分成若干互不相交的子树。树结构描述的是一对多的关系比如文件目录、公司组织架构、HTML 文档结构都属于树形结构。二叉树是树家族中最重要的一种结构每个节点最多有两棵子树分别称为左子树和右子树。二叉树之所以特别重要是因为很多复杂树结构如二叉排序树、哈夫曼树、AVL 树、红黑树都是在二叉树基础上发展而来的。二叉树的重要性质第 i 层最多有 2^(i-1) 个节点。深度为 k 的二叉树最多有 2^k - 1 个节点。对任意一棵二叉树叶子节点数 度为2的节点数 1即 n0 n2 1。最后一个性质经常出现在选择题中需要牢记。4.2 二叉树递归遍历的 C 语言实现二叉树的先序、中序、后序遍历是数据结构课程的必修内容。三者区别只在于访问根节点的时机先序根 → 左 → 右中序左 → 根 → 右后序左 → 右 → 根下面是完整的递归实现// 文件路径binary_tree.c #include stdio.h #include stdlib.h typedef struct TreeNode { char data; struct TreeNode *left; struct TreeNode *right; } TreeNode; // 创建节点 TreeNode* createNode(char data) { TreeNode *node (TreeNode*)malloc(sizeof(TreeNode)); node-data data; node-left NULL; node-right NULL; return node; } // 先序遍历 void preOrder(TreeNode *root) { if (root NULL) return; printf(%c , root-data); preOrder(root-left); preOrder(root-right); } // 中序遍历 void inOrder(TreeNode *root) { if (root NULL) return; inOrder(root-left); printf(%c , root-data); inOrder(root-right); } // 后序遍历 void postOrder(TreeNode *root) { if (root NULL) return; postOrder(root-left); postOrder(root-right); printf(%c , root-data); } int main() { // 构造一棵二叉树 // A // / \ // B C // / \ \ // D E F TreeNode *root createNode(A); root-left createNode(B); root-right createNode(C); root-left-left createNode(D); root-left-right createNode(E); root-right-right createNode(F); printf(先序遍历); preOrder(root); printf(\n); printf(中序遍历); inOrder(root); printf(\n); printf(后序遍历); postOrder(root); printf(\n); return 0; }运行后输出先序遍历A B D E C F 中序遍历D B E A C F 后序遍历D E B F C A递归遍历代码非常简洁但新手容易忽略递归出口。if (root NULL) return;这一行是终止条件一旦遗漏程序会一直递归到栈溢出。4.3 层次遍历队列的实战应用递归遍历依赖函数调用栈而层次遍历需要借助队列来实现。层次遍历的思路是根节点先入队然后循环执行“出队一个节点并访问它再将其左右孩子依次入队”直到队列为空。// 文件路径level_order.c #include stdio.h #include stdlib.h #define MAX_QUEUE 100 typedef struct TreeNode { char data; struct TreeNode *left; struct TreeNode *right; } TreeNode; typedef struct { TreeNode *data[MAX_QUEUE]; int front; int rear; } Queue; void initQueue(Queue *q) { q-front 0; q-rear 0; } int isEmpty(Queue *q) { return q-front q-rear; } void enQueue(Queue *q, TreeNode *node) { if (q-rear MAX_QUEUE) return; q-data[q-rear] node; } TreeNode* deQueue(Queue *q) { if (isEmpty(q)) return NULL; return q-data[q-front]; } TreeNode* createNode(char data) { TreeNode *node (TreeNode*)malloc(sizeof(TreeNode)); node-data data; node-left NULL; node-right NULL; return node; } // 层次遍历 void levelOrder(TreeNode *root) { if (root NULL) return; Queue q; initQueue(q); enQueue(q, root); while (!isEmpty(q)) { TreeNode *current deQueue(q); printf(%c , current-data); if (current-left ! NULL) { enQueue(q, current-left); } if (current-right ! NULL) { enQueue(q, current-right); } } printf(\n); } int main() { TreeNode *root createNode(A); root-left createNode(B); root-right createNode(C); root-left-left createNode(D); root-left-right createNode(E); root-right-right createNode(F); printf(层次遍历); levelOrder(root); return 0; }运行后输出层次遍历A B C D E F层次遍历理解以后图的 BFS 思路也就基本掌握了因为两者本质上是同一个套路队列 逐层扩展。4.4 哈夫曼树与哈夫曼编码哈夫曼树最优二叉树是带权路径长度 WPL 最小的二叉树。构造哈夫曼树的步骤很固定把所有节点按权值从小到大排序每个节点看作一棵树。取出权值最小的两棵树合并为一棵新树新树的权值等于两者之和。把新树放回集合重复第2步直到只剩一棵树。哈夫曼树最重要的应用是哈夫曼编码。把出现频率高的字符用短编码出现频率低的字符用长编码从而压缩数据总长度。哈夫曼编码属于前缀编码任何一个字符的编码都不是另一个字符编码的前缀因此可以无歧义地解码。// 哈夫曼树节点结构体设计 typedef struct { int weight; // 权值 int parent; // 父节点下标-1 表示无父节点 int left; // 左孩子下标 int right; // 右孩子下标 } HTNode;很多同学学哈夫曼树时只记步骤不写代码结果考试时给出一个权值序列就不知道从哪入手。建议亲手模拟一遍“合并最小两棵树”的过程再用代码实现一遍印象会深很多。4.5 树模块常见考点给定先序和中序序列能否唯一确定一棵二叉树能。给定先序和后序序列能否唯一确定不能因为无法确定左右子树边界。n 个节点的二叉链表有多少个空指针域n 1 个。哈夫曼树只有度为 0 和度为 2 的节点没有度为 1 的节点。5. 图从存储到遍历再到算法5.1 图的存储结构图描述的是多对多的关系。图的存储方式主要有两种邻接矩阵用一个二维数组存储顶点间关系。适用于稠密图判断两个顶点是否相邻只需要 O(1) 时间但空间复杂度是 O(n^2)。邻接表用一个顶点数组加若干边链表表示图。适用于稀疏图空间效率高但判断顶点是否相邻需要遍历邻接链表。考研和面试里邻接矩阵的 DFS 和 BFS 最容易手写因为代码直观、不容易出错。邻接表实现稍微复杂重点在于链表操作。5.2 邻接矩阵的 DFS 与 BFS深度优先搜索DFS类似树的先序遍历使用递归或栈实现广度优先搜索BFS类似树的层次遍历使用队列实现。下面是基于邻接矩阵的 DFS 核心代码// 文件路径graph_dfs.c #include stdio.h #define MAX_VERTEX 20 typedef struct { int vertexCount; // 顶点数 char vertex[MAX_VERTEX]; // 顶点数据 int edge[MAX_VERTEX][MAX_VERTEX]; // 邻接矩阵 } Graph; // 从顶点 v 开始深度优先遍历 void dfs(Graph *g, int visited[], int v) { printf(访问顶点 %c\n, g-vertex[v]); visited[v] 1; for (int i 0; i g-vertexCount; i) { // 如果 v 和 i 之间有边且 i 没有被访问过就递归访问 if (g-edge[v][i] 1 visited[i] 0) { dfs(g, visited, i); } } } // 对整个图做 DFS防止有多个连通分量 void dfsTraverse(Graph *g) { int visited[MAX_VERTEX] {0}; for (int i 0; i g-vertexCount; i) { if (visited[i] 0) { dfs(g, visited, i); } } }DFS 和 BFS 的时间复杂度取决于存储结构邻接矩阵O(n^2)邻接表O(n e)5.3 最小生成树与最短路径图模块除了遍历外还有三个核心算法Prim 算法从某个顶点开始每次选一条连接已选顶点集合和未选顶点集合的最小权边适合稠密图。Kruskal 算法每次选一条权值最小且不会成环的边借助并查集判断是否成环适合稀疏图。Dijkstra 算法求单源最短路径核心思想是贪心每次从未确定最短路径的顶点中选择距离源点最近的那个并更新其邻接点的距离。这三种算法都属于“背模板容易理解原理难”的类型。考试中经常要求画图模拟执行过程所以不能只记代码要能在纸上一轮一轮地画出选择结果。5.4 图模块常见考点一个有 n 个顶点的无向图最多有多少条边n(n-1)/2。一个有 n 个顶点的有向图最多有多少条边n(n-1)。拓扑排序在有向无环图中按依赖关系输出顶点的序列。关键路径整个工程的最长路径决定项目最短工期。6. 查找与排序性能优化的核心6.1 折半查找折半查找二分查找要求数据按关键字有序且采用顺序存储。每次将待查找区间缩小一半时间复杂度为 O(log n)。// 文件路径binary_search.c #include stdio.h int binarySearch(int arr[], int n, int target) { int low 0; int high n - 1; while (low high) { int mid low (high - low) / 2; // 防止溢出 if (arr[mid] target) { return mid; } else if (arr[mid] target) { low mid 1; } else { high mid - 1; } } return -1; } int main() { int arr[] {1, 3, 5, 7, 9, 11, 13}; int n sizeof(arr) / sizeof(arr[0]); int index binarySearch(arr, n, 7); printf(7 的下标是%d\n, index); index binarySearch(arr, n, 8); printf(8 的下标是%d\n, index); return 0; }运行后输出7 的下标是3 8 的下标是-1注意二分查找的边界条件low high如果写成low high会漏掉 low 和 high 指向同一个元素的情况。希望用mid low (high - low) / 2而不是(low high) / 2是为了避免两个很大的 int 相加时溢出。6.2 二叉排序树与哈希表二叉排序树BST左子树所有节点值小于根节点右子树所有节点值大于根节点。中序遍历 BST 得到的就是有序序列。但如果插入序列本身有序BST 会退化成链表查找效率降到 O(n)。为了保持平衡才有了 AVL 树和红黑树。哈希表通过散列函数把关键字映射到存储位置理想情况下查找时间复杂度是 O(1)。哈希冲突的常见处理办法有开放定址法和链地址法。链地址法最直观每个散列位置挂一条链表冲突的元素依次挂在链表上。应试时哈希表几乎必考“构造哈希表 计算查找成功和失败的 ASL平均查找长度”。这个计算过程必须在草稿纸上完整走一遍不能只看答案。6.3 快速排序的 C 语言实现排序算法里快排是面试出现频率最高的一种。重点考查每一趟排序后基准元素把序列分成左右两部分左边都比基准小右边都比基准大。下面是经典的挖坑法实现// 文件路径quick_sort.c #include stdio.h void quickSort(int arr[], int low, int high) { if (low high) return; int pivot arr[low]; // 取第一个元素为基准 int i low; int j high; while (i j) { // 从右往左找第一个比 pivot 小的元素填入左边的坑 while (i j arr[j] pivot) { j--; } if (i j) { arr[i] arr[j]; } // 从左往右找第一个比 pivot 大的元素填入右边的坑 while (i j arr[i] pivot) { i; } if (i j) { arr[j--] arr[i]; } } // 基准元素就位 arr[i] pivot; // 递归处理左右两部分 quickSort(arr, low, i - 1); quickSort(arr, i 1, high); } void printArray(int arr[], int n) { for (int i 0; i n; i) { printf(%d , arr[i]); } printf(\n); } int main() { int arr[] {6, 1, 8, 3, 9, 2, 5, 7}; int n sizeof(arr) / sizeof(arr[0]); printf(排序前); printArray(arr, n); quickSort(arr, 0, n - 1); printf(排序后); printArray(arr, n); return 0; }运行后输出排序前6 1 8 3 9 2 5 7 排序后1 2 3 5 6 7 8 9快排平均时间复杂度 O(n log n)最坏情况 O(n^2)空间复杂度 O(log n)。正因为它的平均性能优秀实际语言的内置排序往往也以快排思想为基底。6.4 各排序算法复杂度对比排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性直接插入排序O(n^2)O(n^2)O(1)稳定希尔排序O(n^1.3) 左右O(n^2)O(1)不稳定冒泡排序O(n^2)O(n^2)O(1)稳定快速排序O(n log n)O(n^2)O(log n)不稳定简单选择排序O(n^2)O(n^2)O(1)不稳定堆排序O(n log n)O(n log n)O(1)不稳定归并排序O(n log n)O(n log n)O(n)稳定稳定性需要额外强调稳定的排序算法在关键字相等时能保持原始相对顺序。如果记不住所有排序的稳定性至少先记住四个结论冒泡、插入、归并是稳定的快排、选择、堆排是不稳定的。7. 学完课程后常见问题与排查思路实际学习和上机过程中很多同学会反复被下面这些问题卡住。这里整理一份高频排错清单问题现象常见原因解决思路链表打印时出现死循环反转链表时没有保存 next 节点或链表尾部没有置 NULL检查所有指针赋值确认链表最后一个节点的 next 为 NULL程序运行时栈溢出递归函数缺少递归出口或索引越界后导致无限递归检查递归终止条件先打印中间变量定位溢出位置顺序表插入后值异常移动元素方向写反导致数据被覆盖插入必须从后往前移动元素二分查找死循环边界条件写错如low high写成low high死循环时在循环开头打印 low、high、mid观察区间是否收敛快速排序结果错误先移动 i 还是先移动 j 顺序不对或内层 while 缺少i j记住挖坑法先从右往左找小元素且内外层都要判断i j哈希表查找失败 ASL 算错没有把“比较空位”的次数算进去查找失败时的比较次数要一直到遇到空位为止这些问题的共同根源其实都是对内存和边界条件的理解不够扎实。建议遇到报错后不要急着复制代码先把栈、堆、指针、递归调用栈这些底层概念重新梳理一遍。8. 数据结构实验与上机建议8.1 实验报告怎么写很多同学做数据结构实验时代码写得很快但实验报告随便抄一段导致最后实验分很低。一份好的数据结构实验报告至少应该包含需求分析实验要解决什么问题输入输出格式是什么。概要设计采用什么逻辑结构和存储结构程序分为哪些模块。详细设计核心函数的功能、参数和返回值。调试分析遇到过哪些 bug如何解决的算法的时间和空间复杂度。运行结果贴出输入和输出截图。实验总结学到了什么哪些地方可以优化。如果只是单纯贴代码老师很难看出你的思考过程。尤其“调试分析”部分最能体现一个人是不是真的亲手写过代码。8.2 三个适合练手的小实验如果你不知道从哪个题目开始可以先完成下面三个小实验它们正好覆盖线性表、树、查找排序三个模块学生成绩管理系统用单链表实现学生信息的增、删、改、查并按成绩排序输出。这个实验能锻炼链表操作和文件读写。表达式求值程序输入一个中缀表达式利用栈将其转换为后缀表达式并求值。这个实验能锻炼栈的运用。哈夫曼编码器读入一段文本统计字符频率构造哈夫曼树并生成哈夫曼编码。这个实验综合了二叉树和贪心思想。这三个实验都不需要额外的第三方库用标准 C 语言就能完成适合作为期末课程设计或考研机试的平时训练。9. 最佳实践与工程建议9.1 从“会写代码”到“设计合理”数据结构课程结束后常见的问题是“我明明每个算法都自己实现过但遇到新题目还是不会”。这是因为你只记住了代码模板没有理解算法设计的核心思想。在实际项目或面试中设计一个方案时需要按下面的顺序思考先分析数据量级数据是 100 条还是 1 亿条这决定了 O(n^2) 是否可用。再确定逻辑关系数据是线性、树形还是图形关系线性用数组/链表层次关系用树多对多用图。然后选择存储结构读多写少选顺序存储频繁增删选链式存储精确等值查找考虑哈希表。最后评估算法的稳定性、空间和时间成本在“快”和“省”之间做取舍。这种决策能力就是数据结构课程最想训练的能力。它不是靠背知识点能获得的必须在大量题目中反复练习。9.2 数据结构与考研、面试的衔接考研 408数据结构选择题喜欢考概念细节比如不同排序算法的稳定性、各种树的节点数关系、哈希表 ASL 计算。大题则可能考树的遍历、图的遍历、快排过程模拟等。面试手写题链表反转、合并两个有序链表、判断链表是否有环、二叉树前序/中序/后序遍历的迭代写法、快排、二分查找几乎每场面试都会遇到一两道。软考软考中级“软件设计师”考试中数据结构也是上午题的重点内容包括复杂度分析、二叉树性质、图的基本算法等。如果你有软考计划数据结构这门课一定要认真过一遍。9.3 后续学习路线把哈工大《数据结构》44讲学完后可以根据自己的方向继续深入继续学习算法设计与分析分治、动态规划、贪心、回溯、分支限界。需要应付大厂面试的可以刷LeetCode 热门100题按“数组 → 链表 → 树 → 图 → 动态规划”的顺序刷。对底层感兴趣可以学习STL 源码分析看看 map、set、priority_queue 背后的红黑树和堆是怎么实现的。对实际业务感兴趣可以学习 Redis它内部的跳表、哈希表、字典结构都是数据结构在工程中的经典应用。学习数据结构最大的误区是“只听课不动手”。44讲听完可能只需要几周但真正把它变成自己的东西需要半年以上的持续代码训练。建议每学完一个模块就自己从头默写一遍核心算法而不是打开课件照着敲。默写不出来的地方恰恰就是你最薄弱的地方。如果本文对你有帮助建议收藏备用。接下来挑一个你最容易遗忘的算法亲手从零写一遍吧。