图算法总结

图算法总结

图 (Graph) 是一种复杂的非线性数据结构,由顶点集合及顶点间的关系(也称弧或边)集合组成。可以表示为: G=(V, {VR})
其中 V 是顶点的有穷非空集合; VR 是顶点之间关系的有穷集合,也叫做弧或边集合。弧是顶点的有序对,边是顶点的无序对。
度:无向图中顶点 v 的度是和 v 相关联的边的数目,记为TD(v)。
入度:有向图中以顶点 v 为终点的弧数目称为 v 的入度,记ID(v)。
出度:有向图中以顶点 v 为起点的弧数目称为 v 的出度,记OD(v)。
度:入度和出度之和,即:TD(v) = ID(v) + OD(v)。
如果顶点 vi 的度为 TD(vi),则一个有 n 个顶点e 条边(弧) 的图,满足如下关系:
终端顶点:有向图中把出度为 0的顶点称为终端顶点。
路径长度:路径上边或弧的数目。
回路(环):第一个顶点和最后一个顶点相同的路径。
简单路径:序列中顶点(两端点除外)不重复出现的路径。
简单回路(简单环):前后两端点相同的简单路径。
连通:无向图中从顶点v到v´有路径,则说v和v´是连通的。
连通图:无向图中任意两个顶点都是连通的。
连通分量:无向图的极大连通子图;任何连通图的连通分量只有一个,即其本身;非连通图有多个连通分量(非连通图的每一个连通部分)。
强连通图:有向图G中,若对于V(G)中任意两个不同的顶点vi和vj,都存在从vi到vj以及从vj到vi的路径,则称G是强连通图。
强连通分量:有向图的极大强连通子图;任何强连通图的强连通分量只有一个,即其本身;非强连通图有多个强连通分量。
图的存储结构之数组表示法(邻接矩阵表示法):
对于一个具有n个顶点的图,可用两个数组存储。其中一个一维数组存储数据元素(顶点)的信息,另一个二维数组(图的邻接矩阵)存储数据元素之间的关系(边或弧)信息。
邻接矩阵:设 G = (V, {VR}) 是具有 n 个顶点的图,顶点的顺序依次为 {v1, v2, …, vn},则 G 的邻接矩阵是具有如下性质的 n 阶方阵:

特点:
无向图的邻接矩阵对称,可压缩存储;有n个顶点的无向图需存储空间为 n(n-1)/2。
有向图邻接矩阵不一定对称;有n个顶点的有向图需存储空间为n²,空间复杂度O(n2),用于稀疏图时空间浪费严重。
无向图中顶点vi的度 TD(vi) 是邻接矩阵中第i行1的个数。
网的邻接矩阵可定义为:

图的存储方式—邻接矩阵
邻接矩阵使用场合:
数据规模不大n <= 1000,m越大越好;稠密图最好用邻接矩阵;
图中不能有多重边出现。
图的存储方式—邻接表
邻接表使用场合:
顶点数很多n>1000,边数却不多;
采用邻接表存储后,很多算法的复杂度也都是跟边数有关;
连通性的问题很多情况边数不多,多采用邻接表存储方式。
图论相关算法
广度优先搜索:
通常用队列(先进先出,FIFO)实现
Q={起点s}; 标记s为己访问;
while (Q非空) {
取Q队首元素u; u出队;
所有与u相邻且未被访问的点进入队列;
标记u为已访问;
}
由BFS得到的路径是原图中从S到T的边数最少的路径
广度优先搜索树不唯一
有向图的强连通分量
在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。
求有向图的强连通分量一般采用Kosaraju算法或Tarjan算法,两者的时间复杂度都是O(n+m)。下面着重介绍一下Tarjan算法。
[Tarjan算法]
Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。dfs时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
我们仍然定义dfn[i]为节点i搜索的次序编号(时间戳),low[i]为i或i的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出:
当dfn(u)=low(u)时,以u为根的搜索子树上的所有节点构成一个强连通分量。
[Kosaraju算法]
Kosaraju是基于对有向图及其逆图两次DFS的方法,其时间复杂度也是O(N+M)。kosaraju算法的步骤如下:
(1) 在有向图G上,从某个顶点出发进行DFS,并按其所有邻接点的搜索都完成(即退出DFS函数)的顺序将顶点排列起来。
(2) 在有向图G上,从最后完成搜索的顶点出发,在图G的逆图G’上进行DFS,若此次遍历不能访问到有向图中所有顶点,则从余下的顶点中最后完成搜索的那个顶点出发,继续做逆图中的DFS,直至有向图中的所有顶点都被访问到为止。每一次调用DFS作逆图中的遍历所访问到的顶点集便是有向图G中的一个强连通分量。
拓扑排序
图的结点存在一个拓扑序:如果图中有边(u,v),则u必定排在v的前面;
拓扑排序在有向无环图(DAG)上进行;
拓扑序列不一定唯一;
利用DFS,当节点u已经扩展完成,将标记颜色置为黑时,将u加入已排序列的顶部,搜索完成时,节点序列为拓扑序;
经过拓扑排序的顶点以与其完成时刻相反的顺序出现;
算法
(1) 计算每个点的入度,入度为0的点加入队列Q
(2) 从Q中取出一个点p,输出
(3) 所有与p相邻的点的入度减1。如果新得到了入度为0的点,则加入队列Q。
(4) 转步骤(2), 直到所有点都输出完毕
如果在执行过程中发现找不到入度为0的点, 说明图中存在环
强连通分量
有向图的强连通分量(SCC)指:对于强连通分量里面的任意两个节点u和v,都存在u到v和v到u的路
思路:
对原图G进行DFS,记录各点的结束时间,把原图G的所有边反向为GT,在GT以各点结束时间递减的顺序DFS,则每棵树都是一个强连通分量
即第一遍DFS生成拓扑序,以拓扑序做第二次DFS
缩点操作
生成一个新的有向图Gscc,将每个强连通分量作为新图的一个点,原图连通分量内部的边删除,连通分量之间的边保留
新图必定有拓扑序,即不会出现有向环(DAG)
Gscc称为原图的核心DAG
生成树
最小生成树:
Prim (O(ElogV))
Kruskal (O(ElogE)+O(alpha*E))
算法的选择:
从图的稀疏程度考虑
从问题对边的限制考虑
最小生成树
Prim算法:
(1) 任意选定一点s,设集合S={s}
(2) 从不在集合S的点中选出一个点j使得其与S内的某点i的距离最短,则(i,j)就是生成树上的一条边,同时将j点加入S
(3) 转到(2)继续进行,直至所有点都己加入S集合。
Kruskal算法:
将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边。
最终得到的结果就是最小生成树。
并查集
欧拉路
欧拉路:经过图中每条边恰好一次的路
欧拉回路:起点和终点相同的欧拉路
无向图存在欧拉路的条件
如果图连通,且所有的点都是偶数度,则有欧拉回路。
如果图连通,且恰有两点是奇数度,则有欧拉路。且欧拉路的起止点为这两个奇数度点。
对重边、自环的情况仍适用。
有向图存在欧拉路的条件
如果图连通,且每个点的入度等于出度,则存在欧拉回路。
如果图连通,且恰有一点u的出度比入度大1,另有一点v的出度比入度小1,其余的出度等于出度,则存在欧拉路,起点为u,终点为v。
对重边、自环的情况仍适用。
套圈算法

void Euler(int start) {
        for (int i = 1; i <= E; i ++) {
        if (!visit[i] && from == start) {
            visit[i] = 1;
            Euler(to);
            path[++ top] = i;
        }
        }
}

倒序path[]为欧拉路

本总结主要重点放在知识方面,因为感觉图算法的理论是很重要的(也比较难懂),具体题目见博客图算法题目。

你可能感兴趣的:(ACM,图算法)