有向图4----有向图强连通性分析Kosaraju算法

博客介绍了强连通性、有向图强连通和强连通分量的概念,重点阐述了强连通分量的Kosaraju算法,包括计算有向图的反向图、对反向图进行顶点排序、按逆后序顺序进行dfs搜索等步骤,还对该算法进行了证明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

强连通性:顶点v和w是互相可达的

有向图强连通:任意两个顶点都是强连通的

强连通分量:有向图中相互连通的顶点组成的最大子集

强连通分量--Kosaraju算法

给定一张有向图G,计算其反向图GR

将反向图进行顶点排序,取得逆后序顺序

按照GR逆后序顺序进行dfs搜索,在构造函数中同一个dfs递归调用中访问到的顶点属于同一个强连通分量

给定有向图G

                                              

求出反向图GR

        //有向图反转
	public Digraph reverse(){
		Digraph R = new Digraph(V);
		for(int v=0; v<V; v++){
			for(int w : adj[v]){
				R.addEdge(w, v);
			}	
		}
		return R;
	}

         

对于反向图进行顶点排序  https://blog.youkuaiyun.com/u014106644/article/details/90444801

             //对g的反向图进行顶点排序
		DepthFirstOrder order = new DepthFirstOrder(g.reverse());

按照GR逆后序顺序进行dfs搜索,在构造函数中同一递归调用访问的顶点属于同一强连通分量

public class KosarajuSCC {
	
	private boolean[] marked;
	private int[] id;
	private int count;
	
	public KosarajuSCC(Digraph g){
		marked = new boolean[g.V()];
		id = new int[g.V()];
		//对g的反向图进行顶点排序
		DepthFirstOrder order = new DepthFirstOrder(g.reverse());
		//对逆后序进行dfs 在同一个dfs递归调用中出现的顶点属于同一强连通分量
		for(int s : order.reversePost()){
			if(!marked[s]){
				dfs(g, s);
				count++;
			}
		}
	}

	private void dfs(Digraph g, int s) {
		marked[s] = true;
		id[s] = count;
		System.out.println("s " + s + " " + count);
		for(int v : g.adj(s)){
			if(!marked[v]){
				dfs(g, v);
			}
		}
	}
	
	public boolean stronglyConnected(int v, int w){
		return id[v] == id[w];
	}
	
	public int id(int v){
		return id[v];
	}
	
	public int count(){
		return this.count;
	}
	
	public static void main(String[] args) {
		Digraph g = GraphUtils.getDiGraphPointG3().getG();
		System.out.println(g.toString());
		KosarajuSCC scc = new KosarajuSCC(g);
		System.out.println(Arrays.toString(scc.id));
	}
}

最终结果:

[1, 0, 1, 1, 1, 1, 3, 4, 4, 2, 2, 2, 2]

          

Kosaraju算法证明

dfs路径搜索的特点:

如果存在路径s-->v,则首先dfs(s)开始----dfs(v)开始----dfs(v)结束----dfs(s)结束

1.每个和s强连通的顶点v都会在构造函数调用的dfs(G,s)被访问到

s,v强连通说明s-->v  v-->s  用反证法证明:
假定v没有再dfs(G,s)中访问到,由于s-->v,则必定v在之前已经被访问过;

又因为v-->s,如果v之前访问过则s也必定访问过,因此dfs(G,s)不会调用,即假设错误;

即每个和s强连通的顶点v都会在构造函数调用的dfs(G,s)被访问到

2.构造函数dfs(G,s)中所到达的任意顶点v必定与s是强连通的
由于dfs(G,s)访问到v则必定存在s-->v  只需要证明在G中有v-->s即可;
要证明G中有v-->s,只需要证明在GR中有s-->v
在GR中进行dfs,如果dfs(G,v)在dfs(G,s)之前结束,则说明在GR中存在路径s-->v,有两种情况:
dfs(G,v)在dfs(G,s)调用之前调用,在dfs(G,s)调用之前结束    不可能,因为在G中有s-->v,则GR中有v-->s,即如果dfs(G,v)开始调用,则必定会在递归中调用dfs(G,s),因此不可能在dfs(G,s)调用之前结束;

dfs(G,v)在dfs(G,s)调用之后调用,在dfs(G,s)结束之前结束,该情况说明含有路径s-->v

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值