会用容器类,不等于懂数据结构。append 谁都会调,但什么时候它是一步、什么时候它要搬走整个数组,答案不在 API 文档里,在内存里。这个系列一篇写一种结构,全部拆到引擎盖之下:不背操作清单,画出格子和指针,让每个代价都有出处。

三个贯穿问题.

每种结构在这里都要过同一套审讯,十五篇共用这三个问题:

数据在哪
它占了哪几格内存:连续的一排,还是散落各处靠指针相认?这一格决定了随机访问、cache 命中和搬家代价。
指针指谁
每个指针格里存着谁的地址?增删改说到底都是几根指针的改写——改写的顺序错了,链就断在半路。
代价花在哪
每个操作在格子上走了几步?步数怎么随规模长大?答案用数的,不用背的——必要时用测量收尾。

一套记法.

回答这三个问题,靠一套贯穿全系列的记法。它们不是装饰——图里的每个点、每根箭头,都对应内存里一个真实存在的东西:

系列图例:左边是记录体记法——head 变量盒指向两格节点,焦点指针为橙色;右边是迷你内存表——格子的编号灰色,格子里存的地址绿色 DATA STRUCTURES · 图例 一套记法,贯穿全系列. head 11 23 Val Next 0xC000 0xC010 地址 变量 数据 0x9000 head 0xC000 0xC000 Val 11 0xC008 Next 0xC010 绿 = 地址与指针,橙 = 当页唯一焦点,墨 = 值,灰 = 结构。两种记法,同一份内存。
  • 内存表:地址一列、变量一列、数据一列。格子自己的编号是灰的,格子里存着的编号是绿的——指针没有魔法,地址就是数据。
  • 记录体:结构体画成它在内存里的样子。一格 Val 存值,一格 Next 存指针,那个绿点就是指针本体;末端是一道 nil 斜线;head、cur 这些名字都是装着地址的变量盒。
  • 可步进分镜:过程不靠旁白,靠运动。节点跨拍保持身份,链变了它们就补间到新位置——节奏交给你,按 ← → 步进,或让它自己走一遍:
0xC000110xC01023headValNext

记录体在分镜里活了:一格值、一格指针,head 是个装着 0xC000 的变量盒。

  1. 记录体在分镜里活了:一格值、一格指针,head 是个装着 0xC000 的变量盒。
  2. 橙色永远只给当拍的唯一焦点——这一拍,看住 11 的 Next 里那个 0xC010。
  3. 结构变了,节点会动:23 脱链沉底,11 成为末端,Next 格里换上 nil 斜线。
每一拍只说一件事。可回退,可自动播,打印和 AI 读到的是全部节拍。
  • Go 代码:示例语言用 Go,理由只有一个——指针是显式的。*Nodenil 写的就是图里画的,图码之间零翻译损耗。这不是 Go 教程,能读懂任何 C 系语言就能读懂这里的代码。还有一句钥匙句先撂在这里,指针篇拿它开锁:Go 的赋值和传参只有一种行为——把格子里的数据抄一份;抄的是值还是地址,决定你改得到哪里。
type Node struct {
    Val  int
    Next *Node
}

怎么读.

十五篇按依赖排序,前后互为伏笔:基础复杂度指针)→ 线性数组与切片链表、双向链表、栈与队列)→ 递归与树(递归、树与 BST、遍历、平衡树、堆)→ 散列与图(哈希表、图)→ 收束(排序,全系列在同一个问题上会师)。顺序读收益最大;单篇跳读也成立,用到前篇结论的地方都会指回去。

系列按顺序发布,已上线的篇目在上面是可点的链接,其余随更新陆续点亮。

每篇的节奏一样:先把结构画出来,再让操作在分镜里动起来,最后落到代码——需要裁决的地方,交给测量。图可以放大,分镜可以步进,打印版和 AI 阅读版拿到的是全部节拍,不是一张定格。

引擎盖已经打开,下一篇从「数步骤」开始。