考研复习 Day 18 | 数据结构与算法--图(上)

一、图的基本概念

1.1 图的定义

图G由顶点集V和边集E组成,记为G=(V,E)

要素 说明
V(G) 顶点的有限非空集
E(G) 顶点之间关系的集合

重要 :线性表可以是空表,树可以是空树,但图不可以是空图。顶点集V必须非空,但边集E可以为空。


1.2 基本术语

1. 有向图 vs 无向图

类型 边的形式 说明
有向图 <v, w> v称为弧尾 ,w称为弧头,表示v→w
无向图 (v, w)(w, v) v和w互为邻接点

2. 简单图 vs 多重图

类型 条件
简单图 ① 不存在重复边;② 不存在顶点到自身的边
多重图 两顶点之间边数可超过1条,允许顶点通过边与自身关联

这里仅讨论简单图

3. 顶点的度、入度、出度

图类型 定义 重要结论
无向图 度TD(v) = 依附于v的边数 所有顶点的度之和 = 2|E|
有向图 入度ID(v) = 以v为终点的边数;出度OD(v) = 以v为起点的边数 所有顶点的入度之和 = 出度之和 =|E|

注:符号含义对照表

符号 含义 类型 举例
E 边集(Edge Set) 集合 E = {(A,B), (B,C)}
` E ` 边的条数

很多人会偷懒直接用 E 表示边数,但它本质上是 |E|
顶点v的度:TD(v) = ID(v) + OD(v)

4. 路径与回路

术语 定义
路径 顶点序列 v₀, v₁, ..., vₙ
路径长度 路径上边的数量
回路(环) 第一个顶点和最后一个顶点相同的路径
简单路径 路径中顶点不重复出现
简单回路 除首尾外,其余顶点不重复的回路
距离 从u到v的最短路径的长度(不存在则为∞)

若一个图有n个顶点,且有大于n-1条边 ,则此图一定有环

5. 子图

V' ⊆ VE' ⊆ E,则称G'是G的子图。

类型 定义
生成子图 满足 V(G') = V(G) 的子图

并非V和E的任何子集都能构成子图------E'中的边关联的顶点必须在V'中。

6. 连通性(无向图)

术语 定义
连通 从v到w存在路径
连通图 图中任意两个顶点都是连通的
连通分量 无向图中的极大连通子图

若一个图有n个顶点,边数少于n-1 ,则此图必然是非连通的

7. 强连通性(有向图)

术语 定义
强连通 从v到w从w到v都有路径
强连通图 图中任意一对顶点都是强连通的
强连通分量 有向图中的极大强连通子图

有向图强连通的最少边数:n条(形成一个环)

8. 生成树与生成森林

术语 定义
生成树 连通图的极小连通子图,包含所有顶点和n-1条边
生成森林 非连通图中,每个连通分量的生成树共同构成

区分:极大连通子图(连通分量)要求包含尽可能多的顶点和边;极小连通子图(生成树)要求保持连通且边数最少。

9. 其他术语

术语 定义
完全图 无向:`
稀疏图/稠密图 `
边上带有权值的图
有向树 一个顶点的入度为0,其余顶点的入度均为1的有向图

二、图的存储及基本操作

2.1 邻接矩阵法

复制代码
#define MaxVertexNum 100
typedef struct {
    VertexType vex[MaxVertexNum];              // 顶点表
    EdgeType edge[MaxVertexNum][MaxVertexNum]; // 邻接矩阵(边表)
    int vexnum, arcnum;                        // 当前顶点数和边数
} MGraph;

特点

图类型 邻接矩阵特点
无向图 一定是对称矩阵,第i行(列)非零元素个数 = 顶点i的度
有向图 第i行非零元素个数 = 出度;第i列 = 入度

重要结论

  • 空间复杂度:O(n²)

  • 判断两点是否有边:O(1)

  • 确定图中边数:需遍历整个矩阵

  • 稠密图适合邻接矩阵

  • Aⁿ[i][j] = 从i到j长度为n的路径数量


2.2 邻接表法

复制代码
#define MaxVertexNum 100
typedef struct ArcNode {           // 边表结点
    int adjvex;                    // 邻接点位置
    struct ArcNode *nextarc;       // 下一条边的指针
    // InfoType info;              // 网边权值
} ArcNode;

typedef struct VNode {             // 顶点表结点
    VertexType data;               // 顶点信息
    ArcNode *firstarc;             // 第一条边
} VNode, AdjList[MaxVertexNum];

typedef struct {
    AdjList vertices;              // 邻接表
    int vexnum, arcnum;            // 顶点数和边数
} ALGraph;

特点

图类型 空间复杂度 特点
无向图 O(n + 2|E|) 每条边出现两次
有向图 O(n + |E|) ---
  • 稀疏图适合邻接表

  • 找所有邻接点:邻接表高效;邻接矩阵需扫描整行

  • 判断两点是否有边:邻接表需遍历边表,效率较低

  • 有向图入度:需遍历所有顶点的边表

  • 邻接表的表示不唯一(取决于输入顺序)


2.3 十字链表(有向图)

每条弧用一个弧结点 表示,每个顶点用一个顶点结点表示。

弧结点结构

顶点结点结构

优点:容易找到以v为尾的弧(出度)和以v为头的弧(入度)


2.4 邻接多重表(无向图)

每条边只用一个结点表示,解决了邻接表中一条边用两个结点表示的问题。

边结点结构

顶点结点结构

优点:判断边是否存在只需遍历一次


2.5 四种存储方式对比

存储方式 空间复杂度 找相邻边 删除边/顶点 适用场景 表示唯一性
邻接矩阵 O(n²) 遍历行/列O(n) 删边方便,删顶点需移动数据 稠密图 唯一
邻接表 无向O(n+2|E|),有向O(n+|E|) 方便 不方便 稀疏图 不唯一
十字链表 O(n+|E|) 方便 方便 有向图 不唯一
邻接多重表 O(n+|E|) 方便 方便 无向图 不唯一

三、图的遍历

3.1 广度优先搜索(BFS)

类似于树的层序遍历 ,借助队列实现,按路径长度递增的顺序访问顶点。

复制代码
bool visited[MAX_VERTEX_NUM];

void BFSTraverse(Graph G) {
    for (int i = 0; i < G.vexnum; i++) visited[i] = FALSE;
    InitQueue(Q);
    for (int i = 0; i < G.vexnum; i++)
        if (!visited[i]) BFS(G, i);
}

void BFS(ALGraph G, int i) {
    visit(i); visited[i] = TRUE;
    EnQueue(Q, i);
    while (!QueueEmpty(Q)) {
        DeQueue(Q, v);
        for (ArcNode *p = G.vertices[v].firstarc; p; p = p->nextarc) {
            w = p->adjvex;
            if (!visited[w]) {
                visit(w); visited[w] = TRUE;
                EnQueue(Q, w);
            }
        }
    }
}

性能分析

  • 空间复杂度:O(n)(辅助队列)

  • 时间复杂度:

    • 邻接表:O(n + |E|)

    • 邻接矩阵:O(n²)

BFS求单源最短路径(非带权图):

复制代码
void BFS_MIN_Distance(Graph G, int u) {
    for (int i = 0; i < G.vexnum; i++) d[i] = ∞;
    visited[u] = TRUE; d[u] = 0;
    EnQueue(Q, u);
    while (!QueueEmpty(Q)) {
        DeQueue(Q, u);
        for (w = FirstNeighbor(G, u); w >= 0; w = NextNeighbor(G, u, w))
            if (!visited[w]) {
                visited[w] = TRUE;
                d[w] = d[u] + 1;
                EnQueue(Q, w);
            }
    }
}

广度优先生成树

  • 邻接矩阵存储:生成树唯一

  • 邻接表存储:生成树可能不唯一


3.2 深度优先搜索(DFS)

类似于树的先序遍历 ,借助**递归(栈)**实现,遵循"尽可能深"地探索的策略。

复制代码
bool visited[MAX_VERTEX_NUM];

void DFSTraverse(Graph G) {
    for (int i = 0; i < G.vexnum; i++) visited[i] = FALSE;
    for (int i = 0; i < G.vexnum; i++)
        if (!visited[i]) DFS(G, i);
}

void DFS(ALGraph G, int i) {
    visit(i); visited[i] = TRUE;
    for (ArcNode *p = G.vertices[i].firstarc; p; p = p->nextarc) {
        j = p->adjvex;
        if (!visited[j]) DFS(G, j);
    }
}

性能分析

  • 空间复杂度:O(n)(递归工作栈)

  • 时间复杂度:

    • 邻接表:O(n + |E|)

    • 邻接矩阵:O(n²)

深度优先生成树/森林

  • 连通图:一棵生成树

  • 非连通图:生成森林


3.3 图的遍历与连通性

图类型 遍历特点
无向图 调用BFS/DFS的次数 = 连通分量数
有向图 一次遍历不一定能访问所有顶点,需外层循环确保全覆盖

四、思考

1. 邻接矩阵 ≈ 城市之间的直达航班表

行是出发城市,列是到达城市,单元格填1表示有直达航班。对角线是自己的城市(0),矩阵对称表示往返都有航班。
2. 邻接表 ≈ 微信好友列表

每个用户有一个好友列表(边表),只存储自己的好友,不存储非好友关系。空间利用率高。
3. BFS ≈ 六度分隔理论

你认识的朋友是第一层,朋友的朋友是第二层......BFS正是按这个层层递进的顺序探索"社交网络"中的人际关系。
4. DFS ≈ 走迷宫

沿着一条路一直走,走到死胡同就退回上一个岔路口,换一条路继续走,直到走遍所有路。

注:以上内容参考 2027年数据结构考研复习指导 王道论坛 组编,其中有一些个人想法,如有任何错误或不妥,欢迎各位大佬指出,如果各位有一些有意思的想法,也可以和我交流一下~感谢!


五、明日计划

图(下)--- 最小生成树、最短路径、拓扑排序、关键路径

相关推荐
自我意识的多元宇宙2 小时前
【数据结构】平衡二叉树
数据结构
睡觉就不困鸭2 小时前
第十六天 反转字符串II
数据结构
测绘第一深情2 小时前
租用GPU云服务器进行深度学习(AutoDL,超保姆级,适用新手)
数据结构·人工智能·经验分享·python·深度学习·算法·计算机视觉
北顾笙9802 小时前
day34-数据结构力扣
数据结构·算法·leetcode
自我意识的多元宇宙3 小时前
【数据结构】二叉排序树
数据结构·算法
Epiphany.5563 小时前
树上dp问题
数据结构·算法
自我意识的多元宇宙3 小时前
【数据结构】 散列表
数据结构·散列表
承渊政道3 小时前
【动态规划算法】(简单多状态dp问题入门与经典题型解析)
数据结构·c++·学习·算法·leetcode·macos·动态规划
嘻嘻哈哈樱桃3 小时前
牛客经典101题题解集--哈希
java·数据结构·python·算法·leetcode·职场和发展·哈希算法