图论相关算法1

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

深度优先搜索
相关概念
递归实现 结点颜色:
白色(开始),灰色(发现),黑色(结束)
一个结点总是从白色变为灰色,再变为黑色 当所有点都变为黑色时,遍历结束 时间戳(timestamp): d[u]表示一个结点开始被访 问的时间,f[u]表示一个结点结束访问的时间
深度优先搜索
int timestamp = 0; dfs(int p) { timestamp = timestamp + 1; col[p] = GREY; d[p] = timestamp; for (每个与p相邻的点i) if (col[i] == WHITE) dfs(i); timestamp = timestamp + 1; f[p] = timestamp; col[p] = BLACK; }


(Elementary Graph Algorithms)
图的定义
图是由顶点集合以及顶点间的关系的集合 组成的一种关系的数学表示 G = (V,E) 其中顶点是由有穷非空集合 顶点之间的关系(边)是有穷集合 Path (x , y)表示从x到y的一条单向通路, 它是有方向的
图的分类
中存在指向i祖先的回边。反之,若对于某个顶点v,存 在孩子结点w,且low[w]>=dfn[v],表明w及其子 孙均无指向v的祖先的回边,则该顶点v必为关节点。 具体算法如下:
void dfs(int v) { int i,w,cnum=0; //cnum表示孩子个数 times++; visit[v]=1; dfn[v]=low[v]=times; //记录时间戳 for(i=0;i<map[v].size();i++) { w=map[v][i]; if(!visit[w]) //w没有访问过,则w是v的孩子结点 { cnum++; dfs(w); low[v]=min(low[w],low[v]); if(v==root&&cnum==2) //如果v是根,且有2个以上的孩子,则为关节点 flag[v]=1; if(v!=root&&low[w]>=dfn[v])//丌为根若low[w]>=dfn[v]),则为关节点 flag[v]=1; } else if(v!=w) //w已经访问过了,说明从v到w有一条回边,w是v的祖先 { low[v]=min(low[v],dfn[w]); } }
图的存储表示 邻接矩阵(Adjacency Matrix)
设图G=(V,E)是一个有N个顶点的图,图的 邻接矩阵是一个二维数组G.edge[N][N] 定义:
1 If (i,j) ∈E(G) G.edge[i][j]= 0 otherwise
0
1
0 1 1 1
G.edge[i][j]=
3 2
void solve() { int i; Stop=Bcnt= times =0; memset(dfn,0,sizeof(dfn)); for (i=1;i<=n;i++) if (!dfn[i]) tarjan(i); }
从节点1开始DFS,把遍历到的节点加入栈中。搜索到
节点u=6时,dfn[6]=low[6]=4,找到了一个强连
1 0 0 0 1 0 0 1 1 0 1 0
0
1
2
0 1
0 1 0
G.edge[i][j]= 无向图的临界矩阵是对称的
1 0 0 0
有向图的邻接矩阵往往是不对称的
图的存储表示 邻接表(Adjacency List)
Data adj
0
dest link
1 0 0 0 ∧
dest link
2
dest link
求有向图的强连通分量一般采用Kosaraju算法戒 Tarjan算法,两者的时间复杂度都是O(n+m)。下 面着重介绍一下Tarjan算法。 [Tarjan算法] Tarjan算法是基于对图深度优先搜索的算法,每个强 连通分量为搜索树中的一棵子树。dfs时,把当前搜索 树中未处理的节点加入一个堆栈,回溯时可以判断栈顶
广度优先搜索
由BFS得到的路径是原图中从S到T的边数 最少的路径 广度优先搜索树不唯一
广度优先搜索的例子
0
0 0 1 1
1
1
4 4
2
2 2
1
3 3
2
5 5
2
6 6 7 7
4百度文库
3
8 8
t = 0; void BFS( int s ) // 从 s 开始遍历 { int queue[maxn], tail, head; head = tail = 0; queue[head] = s; depth[s] = 0; time[s] = t++; visit[s] = true; while ( head <= tail ) // queue not empty { int curr = queue[head++]; int i; for (i = 0; i < n; i++) if ( adj[curr][i] && !visit[i] ) { visit[i] = true; depth[i] = depth[curr] + 1; time[i] = t++; queue[++tail] = i; } } }
有向图:图中的边是有方向的。E (x ,y) 和 E ( y ,x)表示的边不同 无向图:图中的边是没有方向的。 完全图:n个顶点的图两两连边,即有 n(n-1)/2条边,则此图为n的完全图。 用K n表示
图的一些概念
邻接顶点:如果(u ,v) ∈E(G),则称u与v互 为邻接顶点 子图:设有两个图G(V,E)和G’(V’,E’),若V’ V 且E’ E’,则称图G‘是图G的子图 权:某些图的边上标有相关的数字,称为 该边的权值
3 ∧
0
1
1
3
2
2
3 2
∧ ∧
3
同一个顶点发出的边链接在同一个边链表 中,每一个链结点代表一条边,结点中另 一个顶点的下表dest和指针link
图论相关算法
广度优先搜索
通常用队列(先进先出,FIFO)实现 Q={起点s}; 标记s为己访问; while (Q非空) { 取Q队首元素u; u出队; 所有与u相邻且未被访问的点进入队列; 标记u为已访问; }
来。如上图中的顶点B、D和G。
我们在DFS的基础上增加一个low数组,low[i]
用来记录i及i的子孙相连的辈分最高的祖先的访问 时间戳。 low[v]=min(dfn[v],low[w],dfn[k]);
w是顶点v在深度优先树上的孩子结点; k是顶点v在深度优先树上由回边联结的祖先结点;
当low[j]<dfn[i](j是i的儿子)时,说明j戒者j的子孙
0
6 1 2 10 3 5 4 9 顶点0、4、5、6、 7和11为关节点。 12 7 8
11
割点、割边以及连通分量
时间戳:dfn[i]表示结点i是第dfn[i]个被访问到的结点。有的时候我 们 还要记录某个结点被遍历并检查完毕的时间。
void dfs(v) { dfn[v]=++times; //记录访问结点的时间戳 visit[v]=1; for 寻找一个v的相邻节点u if (visit[u]==0)then DFS(u); }
J
A L F
M B H K G I D E C
由深度优先生成树可得出两类关节点的 特性:
(1) 若生成树的根有两棵戒两棵以上的子树,则此 根顶点必为关节点。因为图中丌存在联结丌同子树
中顶点的边,因此,若删去根顶点,生成树便变成
生成森林。如上图中的顶点A。 (2) 若生成树中某个非叶子顶点v,它的子树中的任 一结点均没有指向v的祖先的回边,则v为关节点。因 为,若删去v,则其子树和图的其他部分就被分割开
通分量。退栈到u=v为止,{6}为一个强连通分量。
返回节点5,发现dfn[5]=low[5]=3,退栈后{5} 为一个强连通分量。
返回节点3,继续搜索到节点4,把4加入堆栈。发现节
点4存在向节点1的回边,节点1还在栈中,所以 low[4]=dfn[1]=1。节点6已经出栈,丌再访问6, 返回3,(3,4)为树边,所以low[3]=low[4]=1。
深度优先搜索
每个顶点的d[u] < f[u] DFS中,v是u的子孙 d[u]<d[v]<f[v]<f[u]在搜索中发现u时可 以从u出发沿一条完全由白色顶点组成的路 径到达v m条边的连通图,DFS中顺序标号每条边为 1~m,则任意与顶点u关联的所有边(边数 >=2)的标号的GCD为1
A
A
B
L
F
C
D
E M
F
G
H
J
B
I
J
K
H K
D
E
C
L
连通图G
M
G I
G的深度优先生成树
树边:深度优先搜索树中的 实线表示树边,在DFS过程 中,从v点访问u点时,若u 没有被访问过,则边(v,u) 为树边。
回边:深度优先搜索 树中的虚线表示回边, 在DFS过程中,从v点 访问u点时,若u点已 经访问过,则边(v,u) 为回边。
到栈中的节点是否为一个强连通分量。
我们仍然定义dfn[i]为节点i搜索的次序编号(时间戳),
low[i]为i戒i的子树能够追溯到的最早的栈中节点的次 序号。由定义可以得出: 当dfn(u)=low(u)时,以u为根的搜索子树上的所有 节点构成一个强连通分量。
void tarjan(int i) { int j; dfn[i]=low[i]=++times; // 为节点u设定次序编号和 Low初值 instack[i]=true; Stack[++Stop]=i; // 将节点u压入栈中 for (edge *e=V[i];e;e=e->next) { j=e->t; if (!dfn[j]) { tarjan(j); if (low[j]<low[i]) low[i]=low[j]; } else if (instack[j] && dfn[j]<low[i]) low[i]=dfn[j]; } if (dfn[i]==low[i]) // 如果节点i是强连通分量的 根 { Bcnt++; do { j=Stack[Stop--]; instack[j]=false; Belong[j]=Bcnt; }while (j!=i);
不w的祖先时,树边v-w是一座桥。 不割点类似的,我们定义low[]和dfn[]。父子边
e=u→v ,当且仅当low[v] > dfn[u]的时候,e是
割边。
有向图的强连通分量
在有向图G中,如果两个顶点间至少存在一条路径,称两
个顶点强连通(strongly connected)。如果有向图G
的每两个顶点都强连通,称G是一个强连通图。非强连通 图有向图的极大强连通子图,称为强连通分量(strongly connected components)。 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点 1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。
顶点的度 一个顶点V的度是与它相连边的条数, 记为Deg(V). 顶点的入度 一个顶点V的入度是以它为终点有向 边的条数,记为InDeg(V) 顶点的出度 一个顶点V的入度是以它为起点有向 边的条数,记为OutDeg(V) 路径 在图 G=(V, E) 中, 若从顶点 vi 出发, 沿 一些边经过一些顶点 Vp1, Vp2, …, Vpm,到达 顶点Vj。则称顶点序列 (Vi Vp1 Vp2 ... Vpm Vj) 为从顶点Vi 到顶点 Vj 的路径。它经过的边(Vi, Vp1)、(Vp1, Vp2)、...、(Vpm, Vj) 应是属于E的 边。
深度优先搜索
割点与割边
如果从图中删去某点和与该点相关联的边后,图 不再连通,那么这个点叫做割点(cut point)。 如果从图中删去某条边后,图不再连通,那么这 条边叫做割边或桥(bridge)。
关节点:图中的关节点指这样一个顶点, 如果删除它,图就会被分割成至少两个分 离的子图。关节点也称作分离顶点或割点。
桥:图中的桥(bridge)是一条边,如果删除这条边,将 把连通图分离成两个断开的子图。无桥的图称作边连通图 (edge-connected graph)。
0 6 1 2 10 3 5 4 9 7 8 这个图丌是边连通图。 边0-5、6-7和1112(带阴影)为桥。
11
12
在任何DFS树中,当且仅当丌存在回边连通w的子孙
相关文档
最新文档