定义

  • 图(G=(V, E)):由顶点集V(所有结点)和边集E(所有边)组成
  • 图的阶(|V|):图G中顶点的个数

!线性表,树可以为空,图不可为空(至少需要一个顶点)

  • 有向图: 由有向边的顶点V和弧尾E组成的有序对<v, w>组成的集合
  • 无向图: 由无向边的两个邻接点v, w组成的无序对(v, w)组成的集合
  • 简单图: 不存在重复的边顶点到自身的边称为简单图
  • 完全图(n个顶点)
    • 有向完全图: 任意两个顶点之间都存在方向相反的两条弧

!一共有 \(n\times (n-1)\) 条边

  • 无向完全图: 任意两个顶点之间都存在边

!一共有 \(\frac{n\times (n-1)}{2}\) 条边

  • 子图: 一个图的顶点集和边集均为另一个图的子集,且这些顶点和边能构成图,则称其为它的子图
  • 连通图: 从顶点v到顶点w有路径存在

!一个n个结点的无向图要是连通图则至少需要n-1条边
!一个n个结点的有向图要是连通图则至少需要n条边
!无向图从任意顶点出发能深度优先遍历所有结点的必定是连通图

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

!一个有n个顶点的图,最少有1个连通分量,最多有n个连通分量;若为连通图,则只有一个连通分量

  • 极大连通子图:无向图的连通分量,要求该连通子图包含其所有的边
  • 寻找连通分量的方法: 选取一个顶点,以这个顶点作为子图,并逐个添加与这个子图相连的顶点和边,直到所有相连的顶点都加入该子图

!若一个图有n个顶点,且边数小于n-1则它必为非连通图

  • 强连通图: 有向图中,从顶点v到顶点w和从顶点w到顶点v都有路径,则称这两个顶点强连通,任意一对顶点强连通则为强连通图
  • 强连通分量: 有向连通图中的极大强连通子图

!强连通分量一定有环

  • 生成树:包含图全部顶点极小连通子图(有n个顶点但只有n-1条边)
    • 极小连通子图:既保持图连通又要使边数最少的子图

!生成树不是唯一的,并且不是一个连通分量
!生成树是连通的,但没有环

  • 生成森林: 非连通图的各个连通分量的生成树构成非连通图的生成森林

!生成树是对应连通图来说,而生成森林是对应非连通图来说的

    • 图的度: 所有顶点的出度之和
    • 顶点的度: 以该顶点为一个端点的边的数目

!无向图全部顶点的度之和等于边数的2倍
!有向图全部顶点的入度和和出度和相等,且等于边数

  • 权: 每条边上具有某种含义的数值称为该边权值,带有权值的边的图称为带权图(网)
  • 路径长度: 路径上边的数目
  • 路径长度和回路
    • 路径长度:路径上边的数目
    • 回路: 路径上第一个顶点和最后一个顶点相同的路径

!环为有向图中的回路

  • 简单路径: 顶点不重复出现的路径
  • 简单回路: 除第一个和最后一个顶点外,不重复出现的回路
  • 距离:从顶点v到顶点w的最短路径的长度,若路径不存在则距离为∞

存储

邻接矩阵

  • 存储方式: 用一个一维数组存储(顺序存储)图中顶点信息(顶点表),二维数组存储边信息(边表)
  • 特性:
    • 无向图的邻接矩阵是对称矩阵,有向图不是
    • 邻接矩阵存储形式唯一
  • 出入度: 有向图出度为邻接矩阵的行之和入度 为邻接矩阵的列之和对无向图来说,其行(列)之和即为该顶点的度
  • 时间复杂度: 邻接矩阵创建图时初始化邻接矩阵的时间复杂度为 \(O(|V|^2)\)
  • 带权图(网): 带权边存储权值,行列相同(该点自身)的值为0,不存在的边值为∞
  • 邻接矩阵适用于创建稠密图
  • 存储结构定义
#define MaxVertexNum 100

typedef char VertexType; // 顶点的数据类型
typedef int EdgeType;    // 带权边的权值数据类型

typedef struct VertexNode
{
    VertexType data;               // 顶点信息
    EdgeNode *FirstEdge;           // 单链表表头指针
} VertexNode, AList[MaxVertexNum]; // AList是数组类型结构体

typedef struct EdgeNode
{
    int vertex;     // 该弧所指向的顶点位置
    EdgeNode *next; // 指向下一条弧的指针
} EdgeNode;

typedef struct List
{
    AList vertices;         // 邻接表
    int VertexNum, EdgeNum; // 图的顶点数和弧数
} ALGraph;

邻接表

  • 存储方式(树的 孩子表示法): 用一个一维数组存储顶点信息(顶点表),用一个单链表存储边信息(无向图:边表,有向图:出边表)
  • 特性: 邻接表存储形式不唯一,邻接矩阵存储形式唯一
  • 时间复杂度: 邻接表创建图的时间复杂度为 \(O(|V|+|E|)\)
  • 邻接表适用于创建稀疏图 \((|E|\ll |V|^2)\)
  • 存储结构定义
#define MaxVertexNum 100 // 顶点数目最大值

typedef char VertexType; // 顶点的数据类型
typedef int EdgeType;    // 带权边的权值数据类型

typedef struct Matrix
{
    VertexType vertex[MaxVertexNum];           // 顶点表
    EdgeType Edge[MaxVertexNum][MaxVertexNum]; // 边表
    int VertexNum, EdgeNum;                    // 图的顶点数和边数
} AMGraph;

十字链表

  • 优点: 解决了邻接表存储有向图的缺陷:只解决了出边问题,未考虑一个顶点的入度问题

邻接多重表

  • 优点: 解决了邻接表关于无向图的优化,解决了邻接边如果需要修改图中的边或者是查询边则需要遍历链表的缺陷;便于删除、修改图中的边的信息

!邻接多重表与邻接表的差别,在于同一条边在邻接表中用两个节点表示,在邻接多重表中只用一个节点

图的遍历

  • 按照某种搜索方法沿着图中边对所有顶点访问一次且仅访问一次
  • 辅助数组visit[ ]标志顶点是否被访问过,初始为FALSE

广度优先搜索(BFS)

  • 实现形式: 队列,类似于二叉树的层次遍历
  • 遍历过程
#define MAX_VERTEX_NUM

bool visited[MAX_VERTEX_NUM];

void BFSTraverse(Graph G, int v)
{
    InitQueue(Q);
    visit(v); // 访问初始顶点
    visited[v] = true; // 将结点改为已访问
    EnQueue(Q, v);     // 结点入队
    
    while (!IsEmpty(Q))
    {
        DeQueue(Q, v);               // 结点出队
        p = G.vertices[v].FirstEdge; // 访问其边表中连接的下一个结点
        while (p)                    // p不为空
        {
            if (!visited[p->vertex]) // 该结点是否未被访问
            {
                visit(p->vertex);      // 输出结点
                visited[v] = true;     // 修改为已访问
                EnQueue(Q, p->vertex); // 该结点入队
            }
            p = p->next; // 访问其边表中连接的下一个结点
        }
    }
}

1.png2.png

  1. A先进队,修改false为true,A出队,p指针转向A的FirstEdge即B,修改B的false为true,B进队
  2. p指针指向FirstEdge的下一个位置即C,操作同上
  3. 指针指过第3个边指针后为空则进入while(! IsEmpty)判断队列是否为空,不为空则B出队,p指针指向B的firstedge即A,被访问往后移next指向第4个即E,修改false,执行同样操作
  • 特性
    • 邻接表存储的图的BFS生成树不唯一,因邻接表存储形式不唯一
    • 邻接矩阵存储的图的BFS生成树唯一,因邻接矩阵存储形式唯一
    • 广度优先搜索空间复杂度:\(O(|V|)\)(n个顶点均需要入队一次,最坏情况下n个顶点在队列里)
    • 邻接矩阵广度优先搜索时间复杂度:\(O(|V|^2)\)
    • 邻接表广度优先搜索时间复杂度:\(O(|V|+|E|)\)
    • BFS可以解决各边权值相等时单源最短路径问题,真正解决单源最短路径问题用迪杰斯特拉算法

深度优先搜索(DFS)

  • 实现形式: 递归,类似于树的先序遍历
  • 实现过程
#define MAX_VERTEX_NUM

bool visited[MAX_VERTEX_NUM];

void DFSTraverse(Graph G, int v)
{
    visit(v);
    visited[v] = true;           // 将结点改为已访问
    p = G.vertices[v].FirstEdge; // 访问其边表中连接的下一个结点

    while (p)   // p不为空
    {
        if (!visited[p->vertex])    // 结点未被访问过
            DFSTraverse(G, p->vertex);  // 递归结点
        p=p->next;
    }
    
}

3.png4.png

  1. 先访问A,修改访问标记,再访问它的第一条边即B
  2. 执行递归调用即第一步访问B,修改访问标记,此时指针p指向B的第一条边即A,因为A被访问过跳过if语句块指针p指向B的第二条边即C顶点,再执行递归调用
  • 特性:
    • 邻接表存储的图的深度优先生成树不唯一,因为邻接表存储形式不唯一
    • 邻接矩阵存储的图的深度优先生成树唯一,因为邻接矩阵存储形式唯一
    • 深度优先搜索空间复杂度:\(O(|V|)\)(最多需要图中全部n个顶点进栈)
    • 邻接矩阵深度优先搜索时间复杂度:\(O(|V|^2)\)
    • 邻接表深度优先搜索时间复杂度:\(O(|V|+|E|)\)
    • 深度优先搜索可以用来判断一个有向图是否有回路

图的应用

最小生成树

  • 特性:最小生成树是一个连通图但不是连通分量并且生成树不唯一

!当图存在权值相同的多条边时,最小生成树不唯一
!最小生成树的代价(也叫做树的权值)唯一
!BFS生成树的树高比DFS生成树的树高小,当树只有一个顶点时,树高相等

普里姆算法(Prim)

  • 依点找边: 先定点再找与该点有关权值最小的边,被选定的边的起点是属于已被决定的定点,终点是尚未加入最小生成树的定点
  • 时间复杂度: 时间复杂度为 \(O(|V|^2)\),适用于稠密图

克鲁斯卡尔算法(Kruskal)

  • 依边找点:先找权值最小的边,若不构成回路再加入点,重复过程;按照网中边的权值由小到大的顺序,不断选取当前未被选取的边集中权值最小的边,直到选取n-1条边即可
  • 时间复杂度: 时间复杂度为 \(O(|E|log_2|E|)\)

迪杰斯特拉算法

  • 长度递增 的顺序求出图的某顶点到其余顶点的最短路径
  • 时间复杂度: 迪杰斯特拉算法时间复杂度为 \(O(|V|^2)\)
  • 应用
    • 用于解决单源最短路径
    • 不适用于带负权值的边
    • 集合S用来存放已经被决定的点

弗洛伊德算法

  • 时间复杂度: 弗洛伊德算法时间复杂度为 \(O(|V|^3)\)
  • 应用
    • 用于解决各个顶点之间最短路径问题
    • 适用于带负权值的边但不允许包含带负权值边组成的回路

拓扑排序

  • 有向无环图(DAG): 若一个有向图中不存在环,则称为有向无环图

!只有有向无环图才有拓扑序列

  • AOV网: 如果把每个环节看成图中一个顶点,这样的有向图中,用顶点表示活动,弧表示活动之间的优先关系,则这样的有向图称为AOV网,它主要反映工程各个活动之间的制约关系,AOV网的边无权值
  • 时间复杂度: \(O(|V|+|E|)\),由于需要打印所有的顶点,而且要删除边

!DAG图的拓扑排序不唯一,唯一DAG图的拓扑排序也不能确定唯一图
!对一般的图,若邻接矩阵是三角矩阵,则存在拓扑序列,反之不一定

关键路径

  • AOE网: 在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示的活动的网称为AOE网,AOE网实际意义是在AOV网的基础上分析工程的最少需要的时间
  • 关键路径: 活动上权值相加最大的几条路径
  • 关键活动: 关键路径上的活动称之为关键活动

!只有缩短关键路径上共有的关键活动的工期才能减少整个工期

判断一个有向图是否有回路

  • DFS深度优先排序
  • 拓扑排序
  • 关键路径

!关键路径判断是否有环存在争议,在有其余两种的情况下优先不考虑

E.G.

若无向图 \(G=(V,E)\) 中含有 7 个顶点,要保证 \(G\) 在任何情况下都是连通的,则需要的边数最少是 ( )

[A] 6                      [B] 15
[C] 16                    [D] 21
解:

  • 要保证 G 在任何情况下都连通,则最坏情况下,即边最多的情况下连通,可知当 G 减去一个顶点构成完全图时,则在任何位置加上一个顶点后都连通。即为 6 个顶点构成完全图,故选C。

若邻接表中有奇数个边表节点,则 ( )

[A] 图中有奇数个结点                [B] 图中有偶数个结点
[C] 图为无向图                           [D] 图为有向图
解:

  • 在使用邻接表存储无向图时,每条边会被存储两次,所以其边表结点必为偶数个。同理,一个无向图一共有 n(n-1)/2 条边,则其边表一共有 n(n-1) 个结点。故选D

无向图 \(G=(V,E)\),其中 \(V=\{a,b,c,d,e,f\}\),\(E=\{(a,b),(a,e),(a,c),(b,e),(c,f),(f,d),(e,d)\}\)对该图从 a 开始进行深度优先遍历,得到的顶点序列正确的是 ( )

[A] a, b, e, c, d, f            [B] a, c, f, e, b, d
[B] a, e, b, c, f, d            [D] a, e, d, f, c, b
解:

  • 对于A选项,e 到 c 无边,但 e 到 d 和 f 有边,则因先遍历 d 或 f,序列错误;B选项同A,f 到不了 e但可以到 d,序列错误;C选项 b 到 c 无边但可以到 e,序列错误;故答案选D

以下叙述,正确的是 ( )

[A] 只要无向图中没有权值相同的边,则其最小生成树唯一
[B] 只要无向图中有权值相同的边,则其最小生成树一定不唯一
[C] 从n个顶点的连通图中选取n-1条权值最小的边,即可构成最小生成树
[D] 设连通图G含有n个顶点,则含有n的顶点,n-1条边的子图一定是G的生成树
解:

  • B 选项,对于一个本身就是一棵树图,他的最小生成树就是本身且唯一;C 选项,从该图中随便选 n-1 条边,这些边可能是一个回路,不构成最小生成树;D 选项同 C 选项,这些边可能不连通
posted @ 2021-12-23 19:17  絵守辛玥  阅读(326)  评论(0)    收藏  举报