“主包你的数据结构已经入门了,是时候开发原神了”——如果你是最近从各种技术群里刷到这句话,大概率会先笑一下,然后陷入沉默。这个梗的本质其实很真实:数据结构入门,离做出一个大型游戏之间,隔着引擎、图形学、网络同步、玩法系统、美术资源一整套工程链路;但反过来,没有数据结构这个地基,后面这些东西连讨论的资格都没有。
这篇文章不是来带大家玩梗的,而是把“数据结构入门”这件事拆开讲清楚:从知识体系、代码实现、复杂度分析,到 Redis、数据库、游戏开发里真实用到的数据结构形态,再到期末复习和考研备考的考点清单。内容覆盖数组、链表、栈、队列、树、图、哈希表,以及排序和查找算法,全部给出可直接运行的示例代码。读完之后,你至少能回答三个问题:数据结构到底在学什么、学了能干什么、考试和面试会怎么考。
如果你是正在学《数据结构》课程的学生,或者准备考研、准备校招笔试,又或者只是想把 C/Java/Go 里的集合类、Redis 里的底层结构看清楚,这篇文章值得直接收藏。
1. 数据结构知识体系速览
先给一张全局表,把最核心的数据结构、关键操作、时间复杂度和现实应用对应起来。后面每个章节再逐个展开。
| 数据结构 | 核心概念 | 典型操作 | 平均时间复杂度 | 常见应用 |
|---|---|---|---|---|
| 数组 | 连续内存、随机访问 | 按下标读写、遍历 | O(1) 访问,O(n) 插入删除 | 线性表、矩阵、缓存行 |
| 链表 | 节点 + 指针,非连续存储 | 插入、删除、遍历 | O(n) 访问,O(1) 插入删除 | 内存池、LRU、邻接表 |
| 栈 | 后进先出 LIFO | push、pop、peek | O(1) | 函数调用、括号匹配、撤销 |
| 队列 | 先进先出 FIFO | enqueue、dequeue | O(1) | 消息队列、BFS、任务调度 |
| 树 | 分层结构,一对多 | 插入、删除、查找、遍历 | O(logn)(平衡树) | 文件系统、索引、哈夫曼编码 |
| 图 | 顶点 + 边,多对多 | DFS、BFS、最短路径 | 取决于表示方式和算法 | 社交网络、地图导航、依赖分析 |
| 哈希表 | 键值映射,哈希函数 | 插入、删除、查找 | O(1) 平均 | 缓存、字典、Redis Hash |
排序算法是另一个独立重点,后面单独开一节。这里先记住一个判断基准:绝大多数情况下,我们选数据结构不是看它“能不能做”,而是看“在什么数据规模下、以什么操作频率做”。同样是存一堆元素,读多写少用数组,写多读少用链表,按 key 查找用哈希表,要范围查询、要排序用平衡树或跳表。
2. 适合谁学:明确学习边界
这门课的学习人群非常宽,但学习目标完全不同,先定位自己再动手,效率会高很多。
- 在校学生:目标是期末过线、考试拿分,重点在概念、手工模拟过程、经典代码背诵。
- 考研党:目标是 408 数据结构大题,重点在算法设计思路、复杂度分析、王道式题型训练。
- 校招求职者:目标是笔试和面试手撕算法,重点在链表、二叉树、动态规划、哈希、堆等高频题。
- 在职开发:目标是读懂框架源码、优化系统性能,重点在真实工程里每种结构的取舍。
同样要清楚这门课的边界。数据结构不是万能的,它解决的是“数据怎么组织、怎么访问、怎么变化”的问题。写完一个二叉树遍历,不代表你能写出一个文件系统;会用跳表,也不代表你能设计出 Redis 那样的高性能缓存。课内代码和工程代码之间,还隔着内存管理、并发控制、持久化、网络协议这些内容。
另外提醒一点:学习时使用教材或开源项目代码,要注意版权和开源许可。严蔚敏教材的代码用于个人学习没有问题,如果要把阅读 Redis、Linux 内核等开源项目后实现的代码发布或商用,先确认对应的 License 要求,避免侵权。
3. 学习环境准备与开发工具选择
如果你用的是 C 语言版本教材,比如严蔚敏主编的《数据结构(C语言版)》,环境准备非常简单。本质上只需要一个编译器和一个编辑器。
3.1 语言版本选择
- C 语言:考研和期末的主流选择,指针和结构体能让你真正看到内存布局,缺点是代码量偏大。
- Java:适合面向对象思维,学习时可以直接对照
ArrayList、LinkedList、HashMap源码。 - Python:写起来最快,适合验证算法思路,但对底层的理解会被语言自动管理遮住一部分。
- Go:语法简洁,切片、map、链表实现都比较直观,适合后端方向。
不管选哪种语言,建议先确认本机版本:
gcc --version java -version go version python --version如果命令报错,说明对应编译器还没装好。Windows 用户装 MinGW-w64,macOS 用户装 Xcode Command Line Tools,Linux 用户直接使用系统包管理器安装 gcc 即可。
3.2 IDE 与调试工具
不推荐一上来就装大型 IDE,轻量工具足够:
- VS Code + C/C++ 插件:轻量,调试配置简单。
- Dev-C++:老牌 C 语言教学工具,开箱即用。
- CLion:功能全,但体积大,不适合低配机器。
- 在线平台:如果本机环境实在装不上,可以用在线编译器临时验证,但不建议长期依赖。
3.3 学习目录结构建议
data-structures/ ├── linear/ # 数组、链表、栈、队列 ├── tree/ # 二叉树、BST、AVL、堆 ├── graph/ # 邻接矩阵、邻接表、DFS/BFS ├── sort/ # 各种排序实现 ├── search/ # 二分、哈希、平衡树 └── notes/ # 复习笔记和思维导图目录分好,后期复习和实验报告整理会省很多时间,尤其是期末要交“数据结构实验报告”的同学。
4. 核心数据结构逐个攻破
这一节是全文的中心,每类结构给出定义、代码片段、复杂度结论和真实应用,建议边读边把代码复制到本机跑一遍。
4.1 数组与字符串
数组是所有数据结构里最基础的一个。它在内存中是一段连续空间,优点是可以通过下标 O(1) 访问,缺点是插入和删除需要移动元素,平均 O(n)。字符串在 C 语言中是字符数组,在 Java 中是String对象,在 Go 中是只读的string,语言层面有差异,但底层思路一致。
如果是处理“固定长度、按下标访问”的问题,数组永远是最优选。Redis 里的数组主要用于 String 类型的简单场景,以及部分集合的紧凑存储。
4.2 链表
链表解决了数组插入删除需要搬移大量元素的问题,但它牺牲了随机访问。链表的每个节点包含数据和指向下一个节点的指针。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; } void insertAtHead(Node **head, int data) { Node *node = createNode(data); node->next = *head; *head = node; } void printList(Node *head) { while (head != NULL) { printf("%d -> ", head->data); head = head->next; } printf("NULL\n"); } int main() { Node *head = NULL; insertAtHead(&head, 3); insertAtHead(&head, 2); insertAtHead(&head, 1); printList(head); return 0; }运行结果:
1 -> 2 -> 3 -> NULL链表是所有指针类题目的基础,面试里“反转链表”“判断环”“找中间节点”全部从这里出。Go 语言里标准库的list.List是双向链表,Java 里LinkedList也是双向链表,原理和上面这段 C 代码完全一致。
4.3 栈与队列
栈是受限的线性表,只能在一端插入删除,后进先出。队列只能在一端插入、另一端删除,先进先出。用数组实现栈是所有教材的标准例题:
#include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int top; } Stack; void init(Stack *s) { s->top = -1; } int push(Stack *s, int val) { if (s->top >= MAX_SIZE - 1) { return 0; } s->data[++s->top] = val; return 1; } int pop(Stack *s, int *val) { if (s->top == -1) { return 0; } *val = s->data[s->top--]; return 1; } int main() { Stack s; init(&s); push(&s, 10); push(&s, 20); push(&s, 30); int v; while (pop(&s, &v)) { printf("%d ", v); } return 0; }输出:
30 20 10栈在真实系统里的应用远比想象中多:函数调用栈、表达式求值、括号匹配、浏览器的前进后退、JVM 虚拟机栈。队列则是消息队列、线程池任务队列、广度优先搜索的核心结构。理解了这两个结构,再去读任何框架的事件循环和任务队列源码,都会轻松很多。
4.4 树与二叉树
树是考研和面试的重头戏,二叉树又是树的重中之重。先序、中序、后序、层序遍历必须能手写。递归版本的先序遍历如下:
#include <stdio.h> #include <stdlib.h> typedef struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; } TreeNode; TreeNode *createNode(int val) { TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode)); node->val = val; node->left = NULL; node->right = NULL; return node; } void preorder(TreeNode *root) { if (root == NULL) { return; } printf("%d ", root->val); preorder(root->left); preorder(root->right); } int main() { TreeNode *root = createNode(1); root->left = createNode(2); root->right = createNode(3); root->left->left = createNode(4); root->left->right = createNode(5); preorder(root); return 0; }输出:
1 2 4 5 3在此基础上,二叉树相关的常见考点包括:计算深度、判断平衡、层序遍历、最近公共祖先、二叉搜索树 BST、堆与优先队列、哈夫曼树与哈夫曼编码。数据库索引里大量使用 B 树和 B+ 树,本质也是在二叉树上扩展出来的多路搜索树,理解了二叉树的自平衡思路再去读 B+ 树,会顺畅很多。
4.5 图
图是最灵活也最抽象的结构。图的存储方式有两种:邻接矩阵和邻接表。邻接矩阵适合稠密图,查询两个顶点是否相邻是 O(1);邻接表适合稀疏图,遍历邻接顶点更高效。核心遍历算法是 DFS 和 BFS,进阶算法是拓扑排序、最短路径(Dijkstra、Floyd)、最小生成树(Prim、Kruskal)。
图的代码量比树大,这里给出一个最精简的邻接表 DFS 思路:
def dfs(graph, node, visited): if node in visited: return visited.add(node) print(node, end=" ") for neighbor in graph[node]: dfs(graph, neighbor, visited) graph = { 0: [1, 2], 1: [0, 3], 2: [0, 3], 3: [1, 2] } dfs(graph, 0, set())游戏开发里最典型的图算法是 A* 寻路,它是 Dijkstra 的启发式优化,本质上仍然依赖图结构和优先队列。地图、任务依赖、社交关系、推荐系统,全部可以用图建模。
4.6 哈希表
哈希表是把 key 映射到数组下标的表结构。平均 O(1) 的查找性能让它成为使用频率最高的数据结构之一,但哈希冲突是绕不开的问题,常见的处理方式是链地址法和开放定址法。Python 里可以用字典直接演示一个简单哈希表:
class SimpleHashTable: def __init__(self, capacity=16): self.capacity = capacity self.table = [[] for _ in range(capacity)] def _hash(self, key): return hash(key) % self.capacity def put(self, key, value): idx = self._hash(key) for i, (k, v) in enumerate(self.table[idx]): if k == key: self.table[idx][i] = (key, value) return self.table[idx].append((key, value)) def get(self, key): idx = self._hash(key) for k, v in self.table[idx]: if k == key: return v return None ht = SimpleHashTable() ht.put("name", "csdn") print(ht.get("name"))哈希表的工程应用极其广泛:Redis Hash、Java HashMap、Go map、数据库的哈希索引、布隆过滤器底层。面试里高频问题“HashMap 为什么线程不安全”“Redis 扩容为什么卡顿”都要从哈希表的实现细节里找答案。
5. 排序与查找算法专题
数据结构课程的后半段几乎就是排序和查找,这也是期末和考研的必考大块。
5.1 排序算法对比
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|
| 冒泡排序 | O(n^2) | O(n^2) | O(1) | 稳定 |
| 选择排序 | O(n^2) | O(n^2) | O(1) | 不稳定 |
| 插入排序 | O(n^2) | O(n^2) | O(1) | 稳定 |
| 希尔排序 | 约 O(n^1.3) | O(n^2) | O(1) | 不稳定 |
| 归并排序 | O(nlogn) | O(nlogn) | O(n) | 稳定 |
| 快速排序 | O(nlogn) | O(n^2) | O(logn) | 不稳定 |
| 堆排序 | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
实际工程和面试里,快排和归并是最高频的。快排的平均性能最好,但最坏情况退化成 O(n^2);归并排序稳定但需要额外 O(n) 空间;堆排序最坏情况也是 O(nlogn),但常数较大。真题经常问“什么场景选哪种排序”,答案是看数据规模、是否要求稳定、内存是否受限。
5.2 快速排序完整代码
快速排序是“数据结构排序算法”搜索词里的绝对主角,建议完整背诵以下代码:
#include <stdio.h> void quickSort(int arr[], int low, int high) { if (low >= high) { return; } int pivot = arr[low]; int i = low, j = high; while (i < j) { while (i < j && arr[j] >= pivot) { j--; } arr[i] = arr[j]; while (i < j && arr[i] <= pivot) { i++; } arr[j] = arr[i]; } arr[i] = pivot; quickSort(arr, low, i - 1); quickSort(arr, i + 1, high); } int main() { int arr[] = {5, 3, 8, 4, 2, 7, 1}; int n = sizeof(arr) / sizeof(arr[0]); quickSort(arr, 0, n - 1); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } return 0; }输出:
1 2 3 4 5 7 8这段代码是经典的分治思想:选一个基准,把比它小的放左边、比它大的放右边,然后递归处理左右子区间。考试除了要求写出代码,还经常要求画出每趟排序后的数组状态,建议拿小数组手动模拟三遍。
5.3 查找算法与复杂度选择
查找的考点集中在顺序查找、二分查找、二叉排序树和哈希查找。
- 顺序查找:无序表可用,O(n)。
- 二分查找:有序表可用,O(logn),前提是能随机访问,所以数组可以、链表不行。
- 二叉排序树:动态插入删除,平均 O(logn),退化为链时是 O(n),因此要引入平衡树。
- 哈希查找:平均 O(1),但不支持顺序和范围查询。
这四种查找直接决定了很多系统设计题的方向。比如 Redis ZSet 用跳表而不是平衡树,是因为跳表在范围查找和实现难度上有优势;数据库为什么用 B+ 树而不是哈希索引,是因为需要范围扫描和磁盘友好的顺序访问。
6. 从课堂到实战:数据结构在真实系统里的应用
学数据结构最怕的就是“学完不知道用在哪儿”。这一节把课堂知识和真实系统对应起来,“主包,你会刷题了”之后,真的能看懂一些工程代码了。
6.1 Redis 中的数据结构
Redis 是理解“数据结构如何落地”的最佳教材。它的五种基础类型,每一种底层都对应不同的数据结构:
| Redis 类型 | 底层实现思路(随版本变化) | 典型使用场景 |
|---|---|---|
| String | SDS 动态字符串 | 缓存、计数器、分布式锁 |
| Hash | 哈希表 / 紧凑列表 | 存储对象字段 |
| List | quicklist | 消息队列、时间线 |
| Set | 整数集合 / 哈希表 | 去重、交并集运算 |
| ZSet | 跳表 + 哈希表 | 排行榜、延时队列 |
读 Redis 源码之前,如果能把教材里的哈希表、跳表、双向链表、动态字符串先手写一遍,源码会变得非常容易理解。这里需要注意:不同版本 Redis 的实现细节差异很大,实际阅读时要先锁定版本再去看底层结构,不要被网上老版本的分析带偏。
6.2 游戏开发中的数据结构和算法
回到标题那个梗。开发《原神》这类大型游戏,需要的远不止数据结构,但数据结构确实贯穿了游戏引擎的每个模块:
- 场景管理:八叉树、空间哈希表用来做物体空间划分和碰撞检测加速。
- 寻路系统:A* 算法基于图结构,战斗单位寻路必须高效。
- UI 和背包系统:背包道具列表用数组或链表,背包排序用排序算法。
- 技能和 Buff 系统:状态栈、事件队列,本质是栈和队列。
- 资源加载:LRU 缓存淘汰用哈希表 + 双向链表,这正好是经典的数据结构组合题。
理解了这些,就能明白“数据结构入门了”和“可以开发原神了”之间到底差了什么:数据结构只是解决局部问题的工具,而大型游戏是无数个局部系统加内容资产的系统工程。用数据结构知识做一个小游戏 DEMO,比如贪吃蛇、俄罗斯方块、简易寻路演示,才是更务实的过渡路径。
6.3 数据库、操作系统与后端服务
- 数据库索引:B+ 树、跳表、哈希索引,索引原理就是树和哈希表。
- 操作系统:进程调度队列、页表、文件分配表,全是队列、树和链表的变体。
- 后端缓存:LRU、LFU 淘汰策略,底层是哈希表 + 链表。
- 消息队列:生产消费模型,核心是队列;Kafka 的日志分段又用到顺序读写和索引结构。
这些内容看起来和课程作业隔得很远,但只要把课程里的结构搞透,去看这些系统的文档和源码时,会发现到处都是熟悉的面孔。
7. 性能观察:用实验验证时间和空间复杂度
书本上的复杂度是理论值,实际跑起来还要受常数、缓存、编译优化影响。建议动手做一个“不同数据规模下的排序耗时对比”实验,这是最直观的性能观察方式。
#include <stdio.h> #include <stdlib.h> #include <time.h> void bubbleSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main() { int n = 10000; int *arr = (int *)malloc(n * sizeof(int)); if (arr == NULL) { return 1; } for (int i = 0; i < n; i++) { arr[i] = n - i; } clock_t start = clock(); bubbleSort(arr, n); clock_t end = clock(); printf("n=%d 耗时: %.3f 秒\n", n, (double)(end - start) / CLOCKS_PER_SEC); free(arr); return 0; }用同样的测试结构分别跑 n = 1000、5000、10000、50000,观察耗时增长趋势,如果接近 4 倍、25 倍、100 倍增长,就说明这个算法确实接近 O(n^2):n 扩大 k 倍,耗时扩大约 k^2 倍。内存占用可以用操作系统的资源监视器查看,Linux 下可以用/usr/bin/time -v观察最大驻留内存。
需要注意:实际耗时还受输入数据初始有序度影响。快排和冒泡在“几乎有序”的数据上表现差异很大,测试时要区分最好、平均、最坏三种情况,不要拿一组数据就下结论。
8. 期末复习与考研备考重点
8.1 高频考点清单
根据各大高校期末真题和考研大纲,出现频率最高的知识点可以整理成一张清单:
| 模块 | 高频考点 |
|---|---|
| 线性表 | 链表插入删除、头插尾插、循环链表、双向链表 |
| 栈和队列 | 出入栈序列、表达式求值、循环队列判空判满 |
| 树 | 二叉树遍历、根据遍历序还原树、哈夫曼树、平衡因子 |
| 图 | DFS/BFS 序列、最小生成树、Dijkstra、拓扑排序 |
| 查找 | 二分查找、哈希冲突处理、平均查找长度 |
| 排序 | 每趟排序结果、稳定性、复杂度对比 |
期末复习建议以“手工模拟”为主:拿一个小数组或小二叉树,把插入排序每趟结果、快排每趟划分结果、二叉树前中后序遍历序列,全部亲手画一遍。很多同学考试丢分不是不会写代码,而是不会模拟过程。
8.2 经典手写题示例
面试和考研机试里的手写题基本可以归类:
- 链表反转、链表判环、合并两个有序链表。
- 用两个栈实现队列、用队列实现栈。
- 二叉树前中后序遍历的递归与非递归版本、层序遍历。
- 判断一棵树是否是平衡二叉树、求二叉树最大深度。
- 字符串匹配、括号匹配、逆波兰表达式。
- 手写快排、归并、堆排序。
- 手写 Dijkstra、拓扑排序的简版。
这些题目不建议死记,建议按“数据结构 + 算法思想 + 边界处理”三个维度去理解。比如链表反转,核心是三个指针的位移;层序遍历,核心是队列大小快照。
8.3 复习路线建议
- 期末向:以学校教材为主,严蔚敏《数据结构》配合实验报告模板,边写代码边整理易错点。
- 考研向:以王道《数据结构》为主,配合历年真题,重点训练选择题的手工模拟和大题的算法设计。
- 求职向:在课程基础上刷 LeetCode 热题,先数组链表栈,再树和图,最后动态规划,每周固定输出代码总结。
不管哪一个方向,都建议留一个可复用的代码模板库,把上面列出的经典手写题整理成自己的标准答案,考试和面试前只翻模板库就够了。
9. 常见问题与排查方法
学习数据结构时大家遇到的问题非常相似,直接列一个排查表:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 运行报段错误 | 指针未初始化、野指针 | gdb 查看崩溃位置 | 节点先置 NULL,malloc 后判空 |
| 链表丢节点 | 头节点更新没传二级指针 | 打印每个节点地址 | 插入删除头节点时用Node **head |
| 递归栈溢出 | 递归深度过大 | 查看递归层级 | 改迭代、使用显式栈 |
| 排序结果不对 | 循环边界写错 | 用 n=5 小数组逐步打印 | 检查 i、j 边界和等于号 |
| 哈希查询不到值 | 哈希函数冲突处理不完整 | 打印桶内元素 | 检查链地址法插入逻辑 |
| 程序突然卡死 | 死循环 | 打断点看循环变量 | 检查 while 条件更新位置 |
| 编译找不到头文件 | 环境变量或安装路径问题 | 检查 include 路径 | 重新配置编译器或 IDE |
最常见的还是链表和指针问题。建议把所有涉及指针的代码都遵守一个习惯:创建节点后立刻将 next 置空,函数入口处判空,操作完再判空。这样能避免绝大多数内存错误。另一个高发点是递归边界,写递归先写终止条件,再写递归体,顺序不要反。
10. 最佳实践与学习建议
结合很多人的学习轨迹,整理几条真正有效的实践建议。
第一,永远先画图再写码。链表插入删除、树的旋转、图的遍历,全部先在纸上把节点和指针关系画出来,写代码只是把图翻译成语法。任何一道题卡住超过二十分钟,先回去画图。
第二,每个数据结构至少手写三遍。第一遍照着教材写,第二遍合上书写,第三遍用另一种语言写。C 语言跑通后,再用 Java 或 Go 实现一遍,你会发现语言只是外壳,数据结构思想才是内核。
第三,把复杂度分析养成习惯。每写完一个算法,先问自己时间复杂度和空间复杂度是多少,能不能优化。这个习惯,是区分“会写代码”和“懂算法设计”的分水岭。
第四,学完一个结构就去找真实用例。学完链表去看 Redis List,学完哈希表去看 Java HashMap 源码,学完树去看 B+ 树的图解,学完图去看 A* 寻路的文章。把课堂知识和工程代码连起来,才不容易忘。
第五,项目代码保持整洁。建议把实验报告、复习笔记、代码模板分开管理,代码目录按线性表、树、图、排序、查找划分,命名统一。期末复习时直接翻目录,效率比临时翻文件夹高十倍。
最后是关于合规和授权:用教材代码做作业是正常的,但如果你要把学习项目发布到 GitHub 或写成博客,涉及开源项目源码的片段,要标注来源并遵循对应 License。做游戏 DEMO 时,不要直接使用未经授权的商业游戏美术资源和音乐资源,这是学习阶段最容易忽略的边界。
11. 总结与下一步
回到开头那句话。数据结构入门,确实离“开发原神”很远,但数据结构是评估一个开发者基础能力的硬指标:链表反转写不写得出来,二叉树遍历熟不熟练,排序复杂度能不能脱口而出,哈希冲突能不能讲清楚,这些都能在几分钟内被面试官考察出来。从功利角度说,它是笔试和面试的入场券;从长期角度说,它是读源码、做系统设计的底子。
这篇文章建议先做三件事:第一,把第 4 节的六段代码全部在本机跑通;第二,拿 n = 1000、10000、100000 三组数据跑排序耗时实验,直观感受复杂度增长;第三,整理一份自己的手写题模板库。做完这三步,数据结构这门课才算是真正“入门”了。
下一步的方向很清晰:如果你想继续深挖,可以按“数据结构与算法”的路线去刷题,同时挑一个真实系统源码做对照阅读。读源码的顺序可以是:先看 Go 语言标准库的 map 和 list,再看 Redis 的 SDS 和跳表,最后试着自己实现一个带 LRU 缓存的键值存储。到那个时候,你也许还是做不出《原神》,但你已经能看懂很多大型系统是怎么用数据结构解决实际问题的了,这个能力,比玩梗有价值得多。