跳转至

P2-9.2 数组(array)、表(table)、树(tree)、图(graph)的直觉

Section ID: P2-9.2 Version: v2026.07.23

在 P2-9.1 中,我们已经把数据结构看成“数据被组织成什么形状”的问题。现在来广泛比较 AI 实践中经常遇到的四种形状。

数组(array)、表(table)、树(tree)、图(graph)。

这里提供一段基础说明,把 arraytabletreegraph 放在一起比较。即使后面的章节会单独再看图,这四种结构分别在回答什么问题,仍然会回到这里建立的标准。

这四种都是承载数据的方式,但它们回答的是不同的问题。数组问位置与轴,表问行与列,树问层级,图问关系。

本节不要求一次性背下所有数据结构名称,而是比较“什么问题会自然地呼唤什么结构”。如果前一节已经给出“为什么需要数据结构”的大图景,那么这里就是把这张图展开成数组、表、树、图这四种代表视角。先抓住这种区分,后面再次遇到 NumPy 和 Pandas 时,就更容易先想到“现在是在问什么结构问题”,而不是只盯着工具名称。

本节现在要抓住的内容 紧接着会延伸到的问题 之后再次出现的位置
数组、表、树、图是回答不同问题的结构 会延伸到为什么在 P2-9.3 中还要单独再看一次图,尤其是关系表示更重要时 之后会在 NumPy、Pandas、文档结构、关系数据的说明中反复出现
同一份数据也会因为你看的是位置、比较、层级还是关系,而读成不同结构 会延伸到判断当前任务里哪一种结构更自然的标准 之后会在数据集设计、预处理、搜索、推荐、知识图谱说明中再次使用
Chapter 9 训练的是结构选择问题,而不是名称记忆 会延伸到如何更稳妥地阅读 P2-9.4 补充学习中的传统术语列表 它会成为之后不把数据结构术语和实际实践场景混在一起阅读的基础
术语 本节先要抓住的含义
array 按位置和轴来读取值的结构
table 通过行与列交叉来整理值的结构
tree 主要通过父子层级来阅读的结构
graph 主要通过节点和连接关系来阅读的结构
structure question 询问当前数据里到底是位置、行列、层级还是关系更重要的问题

核心判断标准:数组(array)、表(table)、树(tree)、图(graph)的直觉

  • 能把数组、表、树、图区分成不同的数据视角。
  • 能分别用位置与轴、行与列、父子关系、节点与边来解释数组、表、树、图。
  • 能说明在 AI 实践中,token、embedding、dataset、文档结构、知识图谱分别更接近哪一种结构直觉。
  • 能说明同样的信息会因为目的不同而被看成数组、表、树或图。

先抓住的标准

本节最先要抓住的标准,不是 数据结构的名字,而是 现在正在问什么问题

现在问的问题 先想到的结构
这个值在什么位置? 数组(array)
这个值属于哪一行哪一列? 表(table)
上下层是怎么分开的? 树(tree)
什么和什么相连? 图(graph)

也就是说,即使是同样的数据,只要你看的重点变成 位置行列层级关系 中的不同项,更自然的结构也会跟着改变。

三个标准

标准 为什么重要 本节需要达到的理解程度
为什么同样的数据能用多种结构来看 因为一旦问题改变,更容易看清它的结构也会改变 只要理解“同样的信息会因为看的是位置、比较、层级还是关系,而读成不同结构”就足够了
数组、表、树、图之间的区别 它让你按“阅读标准”而不是按“名称”去区分四种结构 只要能分开位置、行列、层级、连接这几种不同视角就足够了
现在首先该记住什么 它能帮助你在后面先想起结构问题,而不是工具名称 先记住每种结构更擅长回答什么问题,再记名称

一次比较四种结构

第一次接触数据结构时,比起名称,更重要的是“它在问什么”。

下面这张图展示了四种结构分别强调哪一种问题。

Array, table, tree, and graph compare different data questions

结构 核心问题 基本单位 在 AI 实践里会看到的例子
数组(array) 这是哪个位置上的值? 索引(index)、轴(axis)、值(value) 向量、矩阵、图像像素、embedding
表(table) 这个值属于哪一行哪一列? 行(row)、列(column)、单元格(cell) CSV 数据集、训练数据、评估结果
树(tree) 上层和下层是怎么分开的? 根(root)、父(parent)、子(child) 目录、文件夹、分类体系、决策流程
图(graph) 什么和什么相连? 节点(node)、边(edge) 链接、推荐关系、知识图谱、搜索连接

这四种结构并不是完全隔离的世界。表中的某一列可以像数组那样计算,树也可以被解释成图的一种特殊形式,甚至图也可以通过组合 Python 字典和列表来简单表示。

这里重要的不是记住“正确答案结构”。而是学会:一旦提的问题不同,数据看起来就会不同。

下面这张图重新整理了从问题走向数据结构的流程。

Choose array, table, tree, or graph by the question

先写问题,再写数据名称。

先写的问题 会想到的结构
是否要按顺序计算数字? 数组(array)
是否要按案例比较属性? 表(table)
是否要看上下层之间的包含关系? 树(tree)
是否要沿着对象之间的连接往下走? 图(graph)

如果把这张表改写成更贴近实践的形式:

AI 实践场景 先读的结构直觉
embedding 向量、图像像素 数组
CSV 文件、训练数据集、实验结果表
目录、文件夹、分类体系
链接、推荐、知识关系

数组(array):按位置和轴来读的数据

数组是按位置处理值的结构。NumPy 文档把 ndarray 解释为一种多维容器,用来承载相同类型和大小的元素。这里把数组理解成下面这样。

数组是一种把数字放在位置和轴上的结构。

一维数组就是一排数字。

问题场景:你想用最小的数字列表示例来确认“按位置读取值”的数组直觉。 输入(input):数字列表 embedding。 期望输出(output):打印第一个值和第三个值。 要确认的概念:在数组里,不只值本身重要,它属于哪个位置也重要。

1
2
3
4
5
# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
embedding = [0.12, -0.03, 0.44, 0.18]

print(embedding[0])
print(embedding[2])

二维数组则可以看成带有行和列的数字网格。

问题场景:你想看一个例子,如何从看起来像二维数组的数字网格里取出某个位置的值。 输入(input):一个 2 行 3 列的数字结构 image_patch。 期望输出(output):两个指定行列位置上的值。 要确认的概念:二维数组会同时读取行和列的位置来找到值。

1
2
3
4
5
6
7
8
# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
image_patch = [
    [0, 20, 40],
    [10, 30, 50],
]

print(image_patch[0][2])
print(image_patch[1][1])

在数组里,重要的不只是值,还有位置。图像像素的位置一变,就成了另一张图;embedding 向量里的数字也必须按固定顺序放置,才能用于计算。

在 AI 实践里,数组直觉经常出现在这些场景中。

  • 把句子变成 token ID 的序列时
  • 把词、句子、图像表示成 embedding 向量时
  • 把多个样本像矩阵那样组合后一起计算时
  • 按高度、宽度、通道这些轴来处理图像数据时

数组更接近 用于数值计算的结构。所以它会在 P2-11 的 NumPy 数组、Chapter 3 的向量和矩阵,以及 Part 4 之后的 tensor 说明里再次出现。

例如,要计算分数平均值时,比起看整张表,直接取出分数数组更简单。

问题场景:你想通过平均值计算例子,看看“只包含数字的结构”为什么对计算更有利。 输入(input):分数列表 scores。 期望输出(output):平均值 average。 要确认的概念:数组直觉在以数值计算为中心的工作里尤其有用。

1
2
3
4
5
# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
scores = [82, 75, 45]

average = sum(scores) / len(scores)
print(average)

在这个例子里,关注点不是学生姓名或标签,而是数字的位置与计算。在这种时刻,就应该先想到数组直觉。

表(table):按行和列来读的数据

表是一种通过行和列来读取数据的结构。Pandas 把 DataFrame 解释成一种二维、可改变大小、并且可以容纳异构数据的表格式数据结构,同时说明它带有行与列这两个带标签的轴。

这里把表理解成下面这样。

表是一种“一个案例是一行、一个属性是一列”的结构。

name age score label
Kim 21 82 pass
Lee 20 75 pass
Park 22 45 fail

在 Python 中,小型表可以通过列表和字典来表示。

问题场景:你想看一个例子,如何遍历像表一样整理好的“按案例组织属性”的数据。 输入(input):包含学生记录的列表 students。 期望输出(output):按顺序打印学生姓名和分数。 要确认的概念:表结构可以理解成“一行就是一个案例,每个键都扮演一列”。

1
2
3
4
5
6
7
8
9
# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
students = [
    {"name": "Kim", "age": 21, "score": 82, "label": "pass"},
    {"name": "Lee", "age": 20, "score": 75, "label": "pass"},
    {"name": "Park", "age": 22, "score": 45, "label": "fail"},
]

for student in students:
    print(student["name"], student["score"])

表里重要的是,一行代表什么,一列代表什么。

在 AI 实践里,表的直觉经常出现在这些场景中。

  • 把 CSV 文件读成数据集时
  • 分开输入特征和目标标签时
  • 按模型或按实验比较训练结果时
  • 检查缺失值、异常值和数据类型时

表更接近 整理案例和属性的结构。做数值计算时,它可以转成数组;但当人需要检查和解释数据时,表通常更容易阅读。

例如,如果想筛出合格学生,比起分数数组,表结构会更自然。

问题场景:你想看一个例子,在同时查看一个案例的多个属性时进行条件过滤。 输入(input):学生记录列表 students。 期望输出(output):只收集了合格学生姓名的 passed_students。 要确认的概念:当需要一起看多个属性来比较或过滤时,表的直觉更有优势。

1
2
3
4
5
6
7
8
# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
passed_students = []

for student in students:
    if student["label"] == "pass":
        passed_students.append(student["name"])

print(passed_students)

在这个例子里,关注点不只是数值计算,还有一个案例所携带的多个属性。因此,行列直觉就很重要。

树(tree):按层级来读的数据

树是一种从根(root)开始,沿着父(parent)-子(child)关系向下展开的结构。NIST 的 Dictionary of Algorithms and Data Structures 把树解释为:可以从根节点访问,并且内部节点拥有一个或多个子节点的结构。

这里把树理解成下面这样。

树是一种从上到下范围逐步收窄的层级结构。

1
2
3
4
5
6
7
study-book
├─ Part 1. Introduction
│  ├─ Chapter 1
│  └─ Chapter 2
└─ Part 2. Foundations
   ├─ Chapter 8
   └─ Chapter 9

在 Python 中,小型树可以用字典和列表表示。

问题场景:你想看一个例子,如何用类似树的数据来表达文档结构这样的层级。 输入(input):拥有根标题和子项列表的 course_tree。 期望输出(output):打印每个 Part 标题。 要确认的概念:树是一种“上级项目下面放着下级项目”的层级结构。

# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
course_tree = {
    "title": "study-course",
    "children": [
        {"title": "Foundations", "children": ["Variables", "Functions"]},
        {"title": "Data Work", "children": ["Tables", "Graphs"]},
    ],
}

for part in course_tree["children"]:
    print(part["title"])

树里重要的是层级和路径。哪个项目属于哪个上级项目、从哪里开始、往哪里往下走,这些都很重要。

在 AI 实践和服务中,树的直觉会出现在这些场景中。

  • 阅读文档目录和章节结构时
  • 处理文件夹和文件路径时
  • 创建分类体系或类别时
  • 理解决策树时
  • 阅读 JSON、HTML 这类嵌套结构时

树更接近 把关系整理成层级的结构。并不是所有关系都适合用树表示,但对于上下级清晰的数据,树的直觉非常合适。

在树里,你会问:某个项目下面有什么? 例如,取出某个 Part 下属的 Chapter 列表。

问题场景:你想看一个例子,如何在树里找到某个上级项目下面的下级列表。 输入(input):course_tree 和要找的项目 "Data Work"。 期望输出(output):"Data Work" 下的下级主题列表。 要确认的概念:在树里,从根开始走到目标路径的感觉很重要。

1
2
3
4
# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
for item in course_tree["children"]:
    if item["title"] == "Data Work":
        print(item["children"])

在这个例子里,重要的不是值的大小,也不是表的列,而是路径。你需要从根开始,一路往下走到目标位置的感觉。

图(graph):按连接来读的数据

图用来表示对象之间的连接。NIST 把图解释成“由边(edge)连接起来的一组项目”,并把每个项目解释成顶点(vertex)或节点(node)。

这里把图理解成下面这样。

图是一种用点和线来表达关系的结构。

1
2
3
4
Kim -- Lee
Kim -- Park
Lee -- Choi
Park -- Choi

在 Python 中,小型图可以写成类似邻接表(adjacency list)的形式。

问题场景:你想看一个最简单的例子,如何把人与人之间的连接表示成图。 输入(input):朋友关系字典 friends。 期望输出(output):打印与 "Kim" 直接相连的人列表。 要确认的概念:图是一种保存并追踪“对象与对象之间连接”的结构。

# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
friends = {
    "Kim": ["Lee", "Park"],
    "Lee": ["Kim", "Choi"],
    "Park": ["Kim", "Choi"],
    "Choi": ["Lee", "Park"],
}

for person in friends["Kim"]:
    print("Kim is connected to", person)

图里重要的不是顺序或层级,而是连接。谁和谁相连、可以沿着什么路径走,这些才是重点。

在 AI 实践和服务里,图的直觉会出现在这些场景中。

  • 沿着文档与文档之间的链接走时
  • 在知识图谱里表达概念关系时
  • 在推荐系统里看用户与物品的连接时
  • 在搜索系统里看文档、关键词、来源之间的连接时
  • 在 RAG 里处理文档片段与元数据的关系时

图更接近 沿着关系往前走的结构。在 P2-9.3 中,我们会从节点和边的视角再更详细地看一次图。

在图里,第一步通常先问:和这个对象直接相连的对象有哪些?

问题场景:你想再看一个例子,如何确认图中某个节点直接相连的邻居。 输入(input):friends["Kim"]。 期望输出(output):与 "Kim" 相连的朋友姓名。 要确认的概念:阅读图时,第一个问题通常是谁是它的直接邻居。

1
2
3
# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
for friend in friends["Kim"]:
    print(friend)

然后会再往前走一步,问:如果沿着这些连接继续走,还能看到什么? 这个问题会在 P2-9.3 里更详细处理。

用四种结构重新看同样的信息

现在我们用四种视角重新看同样的学生数据。

下面这张图展示了:同样的学生数据,如何被读成分数数组、记录表、学校层级、朋友关系。

The same student data can become an array, table, tree, or graph

如果只按顺序看分数,就是数组直觉。如果把学生属性按行和列来看,就是表的直觉。如果看的是学校、班级、学生这样的层级,就是树的直觉。如果看的是学生之间的朋友关系,就是图的直觉。

问题场景:我想确认同一组学生记录,在问题改变时会怎样被读成不同结构。 输入(input):包含姓名、班级、分数、标签、朋友关系的学生列表。 期望输出(output):数组直觉下的平均分、表直觉下的通过学生姓名、树直觉下的班级-学生层级、图直觉下的直接朋友。 要确认的概念:同一份源数据会根据问题是计算、比较、层级还是连接,被重新组织成不同结构。

# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
students = [
    {"name": "Kim", "class": "A", "score": 82, "label": "pass", "friends": ["Lee"]},
    {"name": "Lee", "class": "A", "score": 75, "label": "pass", "friends": ["Kim", "Park"]},
    {"name": "Park", "class": "B", "score": 45, "label": "fail", "friends": ["Lee"]},
]

scores = [student["score"] for student in students]
pass_names = [student["name"] for student in students if student["label"] == "pass"]
school = {
    "School": {
        "Class A": ["Kim", "Lee"],
        "Class B": ["Park"],
    }
}
friends = {student["name"]: student["friends"] for student in students}

print("array question:", sum(scores) / len(scores))
print("table question:", pass_names)
print("tree question:", school["School"]["Class A"])
print("graph question:", friends["Kim"])

重要的不是哪一种表达才是 正确答案。问题一旦变化,结构也会跟着变化。

问题 更自然的结构
想计算分数数字吗? 数组(array)
想比较每个学生的属性吗? 表(table)
想看学校-班级-学生的层级吗? 树(tree)
想看学生之间的关系吗? 图(graph)

在小型练习里,把同样的数据转换成多种结构来读,会很有帮助。

问题场景:你想看一个例子,如何从同一份源数据里,同时做出数组、层级分组和图表示。 输入(input):学生记录列表 students。 期望输出(output):分数数组 scores、按班级分组的姓名 names_by_class、朋友图 friends。 要确认的概念:同样的数据会因为问题不同而重新表示成多种结构。

# 这个例子用来确认数组、表、树和图如何用不同结构看待同一类数据。
students = [
    {"name": "Kim", "class": "A", "score": 82, "friends": ["Lee"]},
    {"name": "Lee", "class": "A", "score": 75, "friends": ["Kim", "Park"]},
    {"name": "Park", "class": "B", "score": 45, "friends": ["Lee"]},
]

scores = [student["score"] for student in students]
names_by_class = {}
friends = {}

for student in students:
    names_by_class.setdefault(student["class"], []).append(student["name"])
    friends[student["name"]] = student["friends"]

print("array:", scores)
print("tree-like grouping:", names_by_class)
print("graph:", friends)

这段代码并不是说“永远只有一种结构才是正确的”。它只是展示:从同一份源数据里,可以分别做出用于计算的数组、层级分组,以及关系图。

一旦改变结构,看见的东西也会变

改变数据结构,并不只是改变存储方式。它还会改变:哪些问题变得更容易回答。

按数组看时,计算会更容易。

按表看时,比对和过滤会更容易。

按树看时,层级和包含关系会更容易。

按图看时,连接和路径会更容易。

在 AI 实践里,这些结构还会互相转换。先按表读进来的数据集数值列,可以转成数组再喂给模型;文档表里的元数据,也可以扩展成图关系;原本像目录那样的树状文档结构,在搜索阶段也可能重新解释成文档片段之间的连接图。

通过案例来看

案例 1. 同一份班级数据,用四种结构来读时会有什么变化?

假设有一份数据,同时包含某个班级里学生的姓名、分数、所属班级和朋友关系。人一开始可能只想把它看成一张表。

但问题一变,容易看清的结构也会变。只想计算分数时,数组最自然;比较学生属性时,表更方便;看学校-班级-学生这种归属体系时,树更合适;沿着朋友关系往下走时,图更直接。

本节展示的正是这种转换。数组、表、树、图不是彼此竞争的“唯一正确结构”,而是擅长回答不同问题的视角。因此,只有能把同一份源数据用多种方式重新读取,后面遇到 AI 数据集、embedding、文档结构、链接关系时,才能更准确地解释它们。

可检查的结果是:代码会不会随着问题变化而改变。如果只取分数列表求平均、按班级分组姓名、查看朋友连接时,各自都有更简洁的表示方式,那就说明“改变结构”这件事是有意义的。

检查清单

  • 能不能用一句话分别区分数组、表、树、图?
  • 能不能解释为什么同样的数据也能用不同结构来读?
  • 能不能说明为什么 CSV 文件和 embedding 向量不是同一种结构问题?
  • 能不能从位置、轴、数值计算的角度解释数组?
  • 能不能从行、列、数据集的角度解释表?
  • 能不能从根、父、子、层级的角度解释树?
  • 能不能从节点、边、关系的角度解释图?
  • 能不能说明同样的数据会因为问题不同而被看成数组、表、树或图?
  • 能不能说明在 AI 实践里,token、embedding、dataset、文档结构、知识图谱分别更接近哪一种结构直觉?
  • 能不能先区分当前问题是在看数组、表、层级还是关系?

来源与参考资料

  • NumPy Developers, The N-dimensional array (ndarray), NumPy v2.5 Manual,确认日期:2026-07-20。用于确认 ndarray 的维度、shape、dtype、索引与切片说明,作为数组直觉的依据。
  • pandas, pandas.DataFrame, pandas 3.0.4 documentation,确认日期:2026-07-20。作为把 DataFrame 说明为具有行和列的二维结构的依据。
  • Paul E. Black, tree, Dictionary of Algorithms and Data Structures, NIST,确认日期:2026-07-20。作为把 tree 说明为具有 root 与 parent-child relationships 的层级结构的依据。
  • Paul E. Black, graph, Dictionary of Algorithms and Data Structures, NIST,确认日期:2026-07-20。作为把 graph 说明为用 nodes 与 edges 表达关系的结构的依据。