首页/数据结构/05-graph/图的存储结构 🔗 在 Obsidian 中打开
数据结构 · 05-graph

图的存储结构

难度 ★★★重要度 ★★★★★ 考查频率 高题型 选择 / 综合应用 数据结构/图邻接矩阵邻接表
速查
邻接矩阵空间 $O(|V|^2)$、判边 $O(1)$、找邻接点 $O(|V|)$,适合稠密图;邻接表空间 $O(|V|+|E|)$、判边/找邻接点 $O(\deg(v))$,适合稀疏图

速查

项目
主题图的存储结构
核心概念用二维数组 / 链表表示图中顶点之间的邻接关系
时间复杂度$O(1)$、$O(|V|)$、$O(|V|+|E|)$、$O(|V|^2)$、$O(\deg(v))$
难度⭐⭐⭐
重要性⭐⭐⭐⭐⭐

核心概念

一、邻接矩阵

定义

用一个二维数组表示图中顶点之间的邻接关系。

#define MaxVertexNum 100
typedef struct {
    char vex[MaxVertexNum];               // 顶点表
    int edge[MaxVertexNum][MaxVertexNum]; // 邻接矩阵
    int vexnum, arcnum;                   // 顶点数、边数
} MGraph;

无权图

$$A[i][j] = \begin{cases} 1, & (v_i, v_j) \in E \text{ 或 } <v_i, v_j> \in E \\ 0, & \text{其他} \end{cases}$$

带权图

$$A[i][j] = \begin{cases} w_{ij}, & (v_i, v_j) \in E \text{ 或 } <v_i, v_j> \in E \\ \infty, & \text{其他} \end{cases}$$

示例

     A --- B
     |   / |
     |  /  |
     C --- D

邻接矩阵:

    A  B  C  D
A [ 0  1  1  0 ]
B [ 1  0  1  1 ]
C [ 1  1  0  1 ]
D [ 0  1  1  0 ]

特点

  • 无向图:对称矩阵,可压缩存储上 / 下三角
  • 空间复杂度:$O(|V|^2)$
  • 适用:稠密图

基本操作复杂度

操作时间复杂度
判断边是否存在$O(1)$
找顶点的邻接点$O(|V|)$
插入 / 删除边$O(1)$
插入 / 删除顶点$O(|V|^2)$

二、邻接表

定义

对每个顶点建立一个单链表,存储其所有邻接点。

// 边结点
typedef struct ArcNode {
    int adjvex;              // 邻接点下标
    struct ArcNode *next;    // 下一条边
    // InfoType info;        // 边权值
} ArcNode;

// 顶点结点
typedef struct {
    char data;               // 顶点信息
    ArcNode *first;          // 第一条边
} VNode;

// 邻接表
typedef struct {
    VNode vertices[MaxVertexNum];
    int vexnum, arcnum;
} ALGraph;

示例

     A --- B
     |   / |
     |  /  |
     C --- D

邻接表:

A → [B] → [C]
B → [A] → [C] → [D]
C → [A] → [B] → [D]
D → [B] → [C]

特点

  • 无向图:边结点数 $= 2|E|$
  • 有向图:边结点数 $= |E|$
  • 空间复杂度:$O(|V| + |E|)$
  • 适用:稀疏图

基本操作复杂度

操作时间复杂度
判断边是否存在$O(\deg(v))$
找顶点的邻接点$O(\deg(v))$
插入 / 删除边$O(\deg(v))$

三、逆邻接表

对有向图,每个顶点的链表存储指向该顶点的边。

A ← B
A ← C
B ← C
B ← D
C ← D

逆邻接表:

A → [B] → [C]      ← 指向A的边
B → [C] → [D]      ← 指向B的边
C → [D]            ← 指向C的边
D                  ← 指向D的边
用途方便求入度(普通邻接表求出度容易、求入度需扫全表)。

四、十字链表(有向图)

typedef struct ArcBox {
    int tailvex, headvex;         // 弧尾、弧头
    struct ArcBox *hlink, *tlink; // 同弧头、同弧尾链
    // InfoType info;
} ArcBox;

typedef struct VexNode {
    char data;
    ArcBox *firstin, *firstout;  // 入弧、出弧
} VexNode;

特点

  • 结合邻接表和逆邻接表
  • 方便求入度和出度
  • 空间:$O(|V| + |E|)$

五、邻接多重表(无向图)

typedef struct ArcNode {
    int ivex, jvex;                // 依附的两个顶点
    struct ArcNode *ilink, *jlink; // 依附于ivex、jvex的下一条边
    // InfoType info;
} ArcNode;

typedef struct {
    char data;
    ArcNode *firstedge;
} VexNode;

特点

  • 每条边只存储一次
  • 方便删除边

存储结构对比

存储方式空间判边存在找邻接点适用场景
邻接矩阵$O(|V|^2)$$O(1)$$O(|V|)$稠密图
邻接表$O(|V|+|E|)$$O(\deg)$$O(\deg)$稀疏图
十字链表$O(|V|+|E|)$$O(\deg)$$O(\deg)$有向图
邻接多重表$O(|V|+|E|)$$O(\deg)$$O(\deg)$无向图

手算示例

例 1:邻接矩阵构造

有向图:

A → B → C
↑       ↓
← ← ← D

邻接矩阵:

    A  B  C  D
A [ 0  1  0  0 ]
B [ 0  0  1  0 ]
C [ 0  0  0  1 ]
D [ 1  0  0  0 ]

例 2:邻接表构造

无向图:

0 --- 1
|   / |
|  /  |
2 --- 3

邻接表:

0 → [1] → [2]
1 → [0] → [2] → [3]
2 → [0] → [1] → [3]
3 → [1] → [2]

例 3:入度出度计算

有向图邻接表:

A → [B] → [C]
B → [C]
C → [A]
D → [A] → [B]
顶点出度入度
A22(来自 C, D)
B12(来自 A, D)
C12(来自 A, B)
D20
口诀邻接矩阵中行和 = 出度列和 = 入度;邻接表中链表长度 = 出度,求入度必须扫描整张表(或用逆邻接表)。

常见考法

考法 1:邻接矩阵画图给出邻接矩阵,画出图。
考法 2:邻接表画图给出邻接表,画出图。
考法 3:空间复杂度问:$n$ 个顶点 $e$ 条边的图,邻接矩阵和邻接表各占多少空间?
答:邻接矩阵 $O(n^2)$,邻接表 $O(n+e)$。
考法 4:操作复杂度问:在邻接表中判断边是否存在的时间复杂度?
答:$O(\deg(v))$。

易错点

注意
  1. 无向图邻接矩阵对称:有向图不一定对称。
  2. 邻接表边结点数:无向图 $2|E|$,有向图 $|E|$。
  3. 度的计算:邻接矩阵行和 = 出度,列和 = 入度。
  4. 邻接表不唯一:邻接点顺序可以不同。
  5. 稠密图用邻接矩阵:$e$ 接近 $|V|^2$ 时。

核心结论

图类型推荐存储空间
稠密图邻接矩阵$O(|V|^2)$
稀疏图邻接表$O(|V|+|E|)$
有向图需入度十字链表$O(|V|+|E|)$
无向图需删边邻接多重表$O(|V|+|E|)$

记忆卡片

邻接矩阵的空间复杂度?
$O(|V|^2)$。
邻接表的空间复杂度?
$O(|V|+|E|)$。
无向图邻接矩阵有什么特点?
对称矩阵,可只存上/下三角。
稠密图用什么存储方式?
邻接矩阵。
邻接表中如何求顶点的出度?
遍历该顶点的边链表,链表长度即出度。
无向图邻接表的边结点总数?
$2|E|$(每条边存两次)。

交互动画 · 邻接矩阵 ↔ 邻接表

无向图 G 邻接矩阵 A[i][j] A B C D ABCD ABCD 0 1 1 0 1 0 1 1 1 1 0 1 0 1 1 0 点击按钮:橙格 = 矩阵中值为 1 的邻接关系,浅橙 = 被扫描的整行(共 |V| 格)
选择一个顶点,对照查看矩阵行与邻接表链表
点击上方按钮开始
示意图:邻接矩阵找邻接点必须扫描整行($O(|V|)$,包含大量 0);邻接表只走链表结点($O(\deg(v))$)——这就是稀疏图偏爱邻接表的原因。

相关知识点

graph-traversal topological-sort adjacency-mulitlist-and-orthogonal

↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。