Posted sanweizuiji

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图相关的知识,希望对你有一定的参考价值。

1 图的定义

1.1 图的定义


注意:线性表可以是空表,树可以是空树,但图不可以是空,即V一定是非空集

1.2 基本概念

简单路径

在路径序列中,顶点不重复出现的路径称为简单路径(除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路)

连通

无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的。若图G中任意两个顶点都是连通的,则称图G为连通图,否则称为非连通图。
有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的。若图中任何一对顶点都是强连通的,则称此图为强连通图。

例 具有 n 个结点的连通图至少有(n-1)条边

连通分量

无向图中的极大连通子图称为连通分量

强连通分量

有向图中的极大强连通子图称为有向图的强连通分量

连通图的生成树

连通图的生成树是包含图中全部顶点的一个极小连通子图。
若图中顶点数为n,则它的生成树含有n-1条边。对生成树而言,若去掉它的一条边,则会变成非连通图,若加上一条边则会形成一个回路

1.3 图和二叉树、树和森林这种结构之间的异同点

2 图的实现(包括邻接矩阵和邻接表)和基本操作

2.1 邻接矩阵

2.2 邻接表

2.3 图采用邻接矩阵邻接表进行存储的差异性

3 图的两种遍历

3.1 广度优先遍历(Breadth First Searh)

以下是伪代码

#define MAX_VERTEX_NUM 100/
bool visited[MAX_VERTEX_NUM];

// 从顶点v出发,广度优先遍历图G
void BFS(Graph G, int v) 
    visit(v);
    // 对v做已访问标志
    visited[v] = true;
    // 顶点 v 入队列 Q
    Enqueue(Q, v);
    while (!isEmpty(Q)) 
        DeQueue(Q, v);//顶点v出队列
        for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) 
            if (!visited[w]) 
                visit(w);//访问顶点w
                visited[w] = true;
                EnQueue(Q, w);//顶点w入队列
            
        
    


void BFSTraverse(Graph G) 
    for (int i = 1; i <= G.vexnum; i++) 
        // 初始化标记数组
        visited[i] = false;
    
    // 初始化辅助队列Q
    InitQueue(Q);
    for (int i = 1; i <= G.vexnum; i++) 
        if (!visited[i])
            BFS(G, i);
    

3.2 深度优先遍历(Depth First Search)

以下是伪代码

#define MAX_VERTEX_NUM 100
bool visited[MAX_VERTEX_NUM];

void DFS(Graph G, int v) 
    // 访问顶点v
    visit(v);
    // 标记已访问
    visited[v] = true;
    // w 为 u 的尚未访问的邻接结点
    for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) 
        if (!visited[w]) 
            DFS(G, w);
        
    


void DFSTraverse(Graph G) 
    for (v = 1; v <= G.versum; v++) 
        visited[v] = false;//初始化访问标记数组
    
    for (v = 1; v <= G.versum; v++) 
        if (!visited[v])
            DFS(G, v);
    

例 BFS 类似于层序遍历,DFS 类似于前序遍历。

4 图的基本应用

4.1 最小支撑树

Prim 算法

从某一个顶点开始构建生成树,每次将代价最小的新顶点纳入生成树,直到所有顶点都纳入为止。
时间复杂度:O(|V|²),适用于边稠密图

例 Prim 算法生成一个最小生成树每一步选择都要满足(边的总数不超过n-1、当前选择的边的权值是候选边中最小的、选中的边加入树中不产生回路)三项原则

Kruskal 算法

每次选择一条权值最小的边,使这条边的两头相通(原本已连通的就不选),直到所有顶点都连通
时间复杂度:O(|E|log2|E|),适用于边稀疏图

4.2 最短路径

BFS 算法

以下是伪代码

// 求顶点u到其他顶点的最短路径
void BFS_Distance(Graph G, int u) 
    // d[i]表示从u到i结点的最短路径
    for (int i = 0; i < G.vexnum; i++) 
        // 初始化路径长度
        d[i] =∞;
        // 最短路径从哪个顶点过来
        path[i] = -1;
    
    d[u] = 0;
    visited[u] = true;
    EnQueue(Q, u);
    while (!isEmpty(Q)) 
        DeQueue(Q, u);//队头元素u出队
        for (w = FirstNeighbor(G, u); w >= 0; w = NextNeighbor(G, u)) 
        	// w 为 u 的尚未访问的邻接结点
            if (!visited[i]) 
                d[w] = d[u] + 1;//路径长度加1
                path[w] = u;//记录前驱
                visited[w] = true;//标记已访问
                EnQueue(Q, w);//顶点w入队
            
        
    

Dijkstra 算法

Floyd 算法

以下是伪代码

// 初始化 A 和 path
// 考虑以Vk作为中转结点
for(int k=0;k<n;k++)
	// 遍历整个矩阵,i为行号,j为列号
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        	// 若以Vk为中转点的路径更短
            if(A[i][j]>A[i][k]+A[k][j])
                A[i][j]=A[i][k]+A[k][j];
                path[i][j]=k;
            
        
    

4.3 拓扑排序

4.4 关键路径


从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动(完成整个工程的最短时间就是关键路径的长度,若关键活动不能按时完成,则整个工程的完成时间就会延长)

求关键路径

事件 vk 的最早发生时间 ve(k) ——决定了所有从v开始的活动能够开工的最早时间

活动 ai 的最早开始时间 e(i) ——指该活动弧的起点所表示的事件的最早发生时间

事件 vk 的最迟发生时间 vl(k) ——它是指在不推迟整个工程完成的前提下,该事件最迟必须发生的时间

活动 ai 的最迟开始时间 l(i) ——它是指该活动弧的终点所表示事件的最迟发生时间与该活动所需时间之差

活动 a 的时间余量 d(i)=l(i)-e(i),表示在不增加完成整个工程所需总时间的情况下,活动 ai 可以拖延的时间

d(k)=0的活动就是关键活动,由关键活动可得关键路径

以上是关于图的主要内容,如果未能解决你的问题,请参考以下文章

欧拉回路混合图的欧拉回路

欧拉回路

欧拉道路与欧拉回路

欧拉回路

欧拉回路

[思维模式-12]:《如何系统思考》-8- 工具篇 - 因果回路图/系统循环图/系统控制图,系统思考的关键工具