算法导论——Kosaraju算法(强连通性)

package org.loda.graph;

import org.loda.util.In;

/**
 *
* @ClassName: KosarajuSCC
* @Description: Kosaraju强连通算法
*
* 理解:原图g,逆后序order中如果a->b,那么反向图rg中如果也有a->b,表示这是强连通的。
* 在反向图中按照原图的逆后序顺序进行dfs的时候,如果能够从a搜到b,那么必然有a->b,这部分必然强连通。并且
* 扩展开来,一次dfs能搜到多少,就表示有这个强连通分量有多大,总共要搜几次就代表一共有多少个强连通分量
*
* @author minjun
* @date 2015年5月24日 下午11:31:05
*
 */
public class KosarajuSCC {

	//强连通数
	private int count;

	//是否被访问
	private boolean[] visited;

	//每个元素所属的强连通分量id
	private int[] id;

	public KosarajuSCC(Digraph g){
		//初始化
		int v=g.v();
		visited=new boolean[v];
		id=new int[v];

		//获取逆后序
		DeptFirstOrder d=new DeptFirstOrder(g);

		//获取g的反向图
		Digraph rg=g.reverse();

		//根据反向图的拓扑顺序进行dfs
		for(int i:d.reversePost()){
			if(!visited[i]){
				dfs(i,rg);
				//每完成一次dfs,表示搜到一次强连通分量,count+1
				count++;
			}
		}
	}

	/**
	 *
	* @Title: dfs
	* @Description: 深度优先搜索
	* @param @param v
	* @param @param g    设定文件
	* @return void    返回类型
	* @throws
	 */
	private void dfs(int v, Digraph g) {
		visited[v]=true;
		id[v]=count;
		for(int w:g.adj(v)){
			if(!visited[w]){
				dfs(w, g);
			}
		}
	}

	/**
	 *
	* @Title: count
	* @Description: 强连通分量数量
	* @param @return    设定文件
	* @return int    返回类型
	* @throws
	 */
	public int count(){
		return count;
	}

	/**
	 *
	* @Title: strongConnected
	* @Description: 判断两点是否强连通
	* @param @param a
	* @param @param b
	* @param @return    设定文件
	* @return boolean    返回类型
	* @throws
	 */
	public boolean strongConnected(int a,int b){
		return id[a]==id[b];
	}

	/**
	 *
	* @Title: id
	* @Description: 所属强连通分量的标识
	* @param @param a
	* @param @return    设定文件
	* @return int    返回类型
	* @throws
	 */
	public int id(int a){
		return id[a];
	}

	/**
	 *
	* @Title: printSCC
	* @Description: 打印所有的强连通分量
	* @param     设定文件
	* @return void    返回类型
	* @throws
	 */
	public void printSCC(){
		StringBuilder[] sb=new StringBuilder[count];
		for(int i=0;i<sb.length;i++){
			sb[i]=new StringBuilder();
		}

		for(int i=0;i<id.length;i++){
			sb[id[i]].append("\t"+i);
		}

		for(StringBuilder s:sb){
			System.out.println("连通分量:"+s.toString());
		}
	}

	public static void main(String[] args) {
		Digraph d=new Digraph(new In("F:\\算法\\attach\\tinyDG.txt"));

		KosarajuSCC k=new KosarajuSCC(d);

		System.out.println("强连通分量数量为:"+k.count());

		//打印强所有连通分量
		k.printSCC();
	}
}

引入的文本数据为:

13
22
 4  2
 2  3
 3  2
 6  0
 0  1
 2  0
11 12
12  9
 9 10
 9 11
 7  9
10 12
11  4
 4  3
 3  5
 6  8
 8  6
 5  4
 0  5
 6  4
 6  9
 7  6

---------------------华丽的分割线------------------

输出内容:

强连通分量数量为:5
连通分量:	7
连通分量:	6	8
连通分量:	9	10	11	12
连通分量:	0	2	3	4	5
连通分量:	1
时间: 2024-10-10 07:48:47

算法导论——Kosaraju算法(强连通性)的相关文章

Kosaraju 算法检测有向图的强连通性

给定一个有向图 G = (V, E) ,对于任意一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的,则说明该图 G 是强连通的(Strongly Connected).如下图中,任意两个顶点都是互相可达的. 对于无向图,判断图是否是强连通的,可以直接使用深度优先搜索(DFS)或广度优先搜索(BFS),从任意一个顶点出发,如果遍历的结果包含所有的顶点,则说明图是强连通的. 而对于有向图,则不能使用 DFS 或 BFS 进行直接遍历来判断.如下图中,

算法导论--贪心算法与动态规划(活动选择问题)

活动选择问题 有一个教室,而当天有多个活动,活动时间表如下:找出最大兼容活动集!活动已按结束时间升序排序. 动态规划 采用动态规划需要满足两个条件:1.最优子结构2.子问题重叠 令Sij表示在ai结束后和aj开始前活动的集合,假定Aij为活动集合Sij的最大兼容子集,其中包含活动ak.问题变成求Sik与Skj最大兼容活动子集Aik与Akjz.我们用c[i,j]表示Sij的最优解的大小. 则c[i,j] = c[i,k]+c[k,j]+1;最后我们需要遍历所有可能的k值,找出最大的一个划分作为c[

重读算法导论之算法基础

重读算法导论之算法基础 插入排序 ? 对于少量数据的一种有效算法.原理: 整个过程中将数组中的元素分为两部分,已排序部分A和未排序部分B 插入过程中,从未排序部分B取一个值插入已排序的部分A 插入的过程采用的方式为: 依次从A中下标最大的元素开始和B中取出的元素进行对比,如果此时该元素与B中取出来的元素大小关系与期望不符,则将A中元素依次向右移动 ? 具体代码如下: public static void insertionSort(int[] arr) { // 数组为空或者只有一个元素的时候不

【强连通分量】tarjan算法及kosaraju算法+例题

阅读前请确保自己知道强连通分量是什么,本文不做赘述. Tarjan算法 一.算法简介 Tarjan算法是一种由Robert Tarjan提出的求有向图强连通分量的时间复杂度为O(n)的算法. 首先我们要知道两个概念:时间戳(DFN),节点能追溯到的最早的栈中节点的时间戳(LOW).顾名思义,DFN就是在搜索中某一节点被遍历到的次序号(dfs_num),LOW就是某一节点在栈中能追溯到的最早的父亲节点的搜索次序号. Tarjan算法是基于深度优先搜索的算法.在搜索过程中把没有Tarjan过的点入栈

算法导论----贪心算法,删除k个数,使剩下的数字最小

先贴问题: 1个n位正整数a,删去其中的k位,得到一个新的正整数b,设计一个贪心算法,对给定的a和k得到最小的b: 一.我的想法:先看例子:a=5476579228:去掉4位,则位数n=10,k=4,要求的最小数字b是n-k=6位的: 1.先找最高位的数,因为是6位数字,所以最高位不可能在后5位上取到(因为数字的相对顺序是不能改变的,假设如果取了后五位中倒数第5位的7,则所求的b就不可能是6位的了,最多也就是4位的79228)理解这点很重要!所以问题变成从第1位到第k+1(n-(n-k-1))取

[算法导论]贪心算法(greedy algorithm)

转载请注明出处:http://www.cnblogs.com/StartoverX/p/4611544.html 贪心算法在每一步都做出当时看起来最佳的选择.也就是说,它总是做出局部最优的选择,寄希望(证明)这样的选择能够导致全局最优解. 贪心算法和动态规划都依赖于最优子结构,也就是一个问题的最优解包含其子问题的最优解.不同的是,动态规划通常需要求解每一个子问题,通过对所有子问题的求解得到最终问题的解.而贪心算法寄希望于通过贪心选择来改进最优子结构,使得每次选择后只留下一个子问题,大大简化了问题

算法导论贪心算法之活动选择

活动选择问题的递归调用算法 #include<iostream> using namespace std; #define N 11 struct node{ int id; int satrt; int end; }A[N+1]; void recursive_activity_seclect(int k,int n) { int m=k+1; while(m<=n&&A[m].satrt<A[k].end) m=m+1; if(m<=n) { cout&l

算法导论之最近顶点对

算法导论在计算几何学这章给出了最近顶点对的求法:采用典型的分治算法 (1)分解:将所有顶点按照x坐标排序后大致分为俩个大小相等的集合L和R (2)求解:分别求出L和R集合中的最小具体,并取二者的较小值为当前的最小值ans (3)合并:对于分属于两个集合的点,每次各取出一个点,计算两点的距离,每次与ans比较去较小值来更新ans的值,并且可以进行优化,具体的优化步骤一共有3个,具体见算法导论. 算法的运行时间为O(n log n),具体代码如下: #include <cstdio> #inclu

强联通分量之kosaraju算法

首先定义:强联通分量是有向图G=(V, E)的最大结点集合,满足该集合中的任意一对结点v和u,路径vu和uv同时存在. kosaraju算法用来寻找强联通分量.对于图G,它首先随便找个结点dfs,求出每个节点最后一次访问的时间戳f(x),然后我们建立反图Gt,接着根据倒序的时间戳来dfs每个节点,每次dfs到的结点集合就是一个强联通分量.事实上这个算法的思想和拓扑排序类似. 我们来证明它(注意这里面的图指原图,而不是反图): 引理:对于G中的两个强联通分量C和C',若点u属于C,点v属于C',且