【算法概论】3.图的分解

来源:互联网 发布:访客网络设置上网时间 编辑:程序博客网 时间:2024/06/02 08:10

3 图的分解

3.1 为什么是图

  • 图的表示:|E||V||E||V|2

    1. 邻接表:|V|u(u,v)O(|E|)
    2. 邻接矩阵:O(1)O(n2)

3.2 无向图的深度优先搜索

  • “边”的分类:树边、回边
  • “深度优先搜索”树
procedure explore(v)  Input: G=(V,E) is a graph;v ∈ V  Output:visited(u) is set to true for all nodes u reachable from v  visited(v)=true;  previsit(v)  for each edge (v,u) ∈ E    if not visited(u): explore(u)  postvisit(v)
  • “深度优先搜索”森林,复杂度O(|V|+|E|)
procedure dfs(G)  for all v ∈ V    visited(v)=false  for all v ∈ V    if not visited(v): explore(v)
  • 无向图的连通性:一个无向图是连通的当且仅当在任意两个定点之间存在一条可达路径
  • 连通部件(connected component)测试
procedure previsit(v)  Input: cc - 初始化为0,dfs每次调用explore(v)加1         ccnum[v] - 存储顶点v的连通部件号  ccnum[v]=cc
  • 前序和后序时间戳:初始时钟clock设置为1
procedure previsit(v) //v顶点进栈时间  pre[v]=clock  clock=clock+1procedure postvisit(v)//v顶点出栈时间  post[v]=clock  clock=clock+1

3.3 有向图的深度优先搜索

  • 边(u,v)的分类

    1. 树边:DFS森林的组成部分[u [v v] u]
    2. 前向边:DFS树中从一个顶点指向其叶子节点的边[u [v v] u]
    3. 回边:DFS树中从一个顶点指向其祖先的边[v [u u] v]
    4. 横跨边:指向一分支的子节点的边 [v v ]、[u u]
  • 有向无环图:存在一个环,当且仅当深度优先搜索过程中探测到一条回边

3.4 强连通部件

  • 强连通部件:有向图中,u到v、v到u均存在路径
  • 强连通部件组成超级节点,对应的图为“有向无环图”(“有向无环图”能够被线性化)

3.4.1 有向图的强连通部件的分解

Korasaju算法

Kosaraju算法的分析和证明_silverbullettt

原理:一个图的强连通部件与它的反向图的强连通部件相同。

![3.1 图的分解强连通部件](3.1 图的分解强连通部件.png)

GT=(V,ET)ET={(u,v):(v,u)E}

//Kosaraju算法邻接矩阵实现static int cnt, cntR, pre[MAXV], post[MAXV];static int num = G->V;//顶点个数int Kosaraju(Graph G){    int v;    //初始化全局变量    cnt = cntR = 0;    for (v = 0; v < num; ++v) {        pre[v] = post[v] = -1;    }    //第一次DFS,计算逆图的后序编号    for (v = 0; v < num; ++v) {        if (pre[v] == -1)            dfsPOST(G, v);    }    cnt = 0;    for (v = 0; v < num; ++v) {        G->sv[v] = -1; //G->sv[v] 表示顶点v的强连通分量编号    }    //第二次DFS,强连通分量编号    for (v = num - 1; v >= 0; --v) {        if (G->sc[post[v]] == -1) {            dfsSC(G, post[v]);            ++cnt;//对一棵树编号之后计数器值加1        }    }    return cnt;//返回强连通分量个数}void dfsPOST(Graph G, int v) //对逆图后序编号{    int t;    pre[v] = cnt++;    for (t = 0; t < num; ++t) {        if (G->adj[t][v] == 1) { //G->adj[t][v]逆图的边            if (pre[t] == -1) //搜索的顶点顺序:节点是否访问                dfsPOST(G, t);        }    }    post[cntR++] = v;}void dfsSC(Graph G, int v){    int t;    G->sc[v] = cnt;    for (t = 0; t < num; ++t) {        if (G->adj[v][t] == 1) {            if (G->sc[t] == -1) //搜索的顶点顺序:逆图后序编号的逆序                dfsSC(G, t);        }    }}int GraphSC(Graph G, int s, int t){    return G->sc[s] == G->sc[t];}

Tarjan算法

algorithm tarjan is    input:graph G=(V,E)    output:set of strongly connected componentsindex:=0S:=emptyfor each v in V do    if(v.index is undefined) then        strongconnect(v)    end ifend forfunction strongconnect(v)    //Set the depth index for v to the smallest unsed index    v.index:=index    v.lowlink:=index    index:=index+1    S.push(v)    v.onStack:=true    //Consider successors of v    for each(v,w) in E do        if(w.index is undefined) then            //Successor w has not yet been visited;recurse on it            strongconnect(w)            v.lowlink:=min(v.lowlink,w.lowlink)        else if(w.onStack) then            //Successor w is in stack S and hence in the current SCC            v.lowlink:=min(v.lowlink,w.lowlink)        end if    end for    //If v is a root node,pop the stack and genetate an SCC    if(v.lowlink==v.index) then        start a new strongly connected component        repeat            w:=S.pop()            w.onStack:=false            add w to current strongly connected component        while(w!=v)        output the current strongly connected component    end ifend function

Gabow算法

The algorithm performs a depth-first search of the given graph G, maintaining as it does two stacks S and P (in addition to the normal call stack for a recursive function). Stack S contains all the vertices that have not yet been assigned to a strongly connected component, in the order in which the depth-first search reaches the vertices. Stack P contains vertices that have not yet been determined to belong to different strongly connected components from each other. It also uses a counter *C*of the number of vertices reached so far, which it uses to compute the preorder numbers of the vertices.

When the depth-first search reaches a vertex v, the algorithm performs the following steps:

  1. Set the preorder number of v to C, and increment C.
  2. Push v onto S and also onto P.
  3. For each edge from v to a neighboring vertex w:If the preorder number of w has not yet been assigned, recursively search w;Otherwise, if w has not yet been assigned to a strongly connected component:Repeatedly pop vertices from P until the top element of P has a preorder number less than or equal to the preorder number of w.
  4. If v is the top element of P:Pop vertices from S until v has been popped, and assign the popped vertices to a new component.Pop v from P.

The overall algorithm consists of a loop through the vertices of the graph, calling this recursive search on each vertex that does not yet have a preorder number assigned to it.

1 0
原创粉丝点击