第五周作业——有向图强连通分量的编程实现

作业要求: 对tinyDG.txt(http://pan.baidu.com/s/1o6jWtcA)文件所表示的图,输出其反向图中顶点post的逆序表示,并输出每一个强连通分量,输出图的超图(思考,or实现),类名GraphSCC



找出一个有向图的所有强连通分量(SCC-Strong Connectivity Component),关键是:

1. 生成该图的反向图(请看:第五周作业——有向图邻接表表示及反向图构造);

2. 对反向图进行DFS(请看:第四周作业——无向图的DFS算法);

3. 对反向图进行post值降序(请看:第三周作业——冒泡排序和归并排序),再到原图中DFS.


将以上三步用到的知识点综合,用Java实现有向图强连通分量的表示,代码如下:

/**
 * 有向图的强连通分量
 * SCC-Strong Connectivity Component
 * 
 * 有向图G(V,E)的强连通分量发现算法:
 * (a)生成G的反向图Gr;
 * (b)对反向图Gr进行DFS搜索,得到顶点post的逆序数组;
 * (c)按照顶点post的逆序,在原图中进行深度优先遍历。
 */
public class GraphSCC {
    //所有顶点的集合
	private GraphNode[] nodes ;	
	//强连通分量集合
	private List<GraphNode[]> scc = new ArrayList<GraphNode[]>();
	
	private boolean[] visited;
	
	//pre和post指针
	private int _pre_post = 1;
	
	//连通分量指针,指向每个连通分量在顶点集合nodes里的开始位置
	private int _cc = 0;
	
	//邻接表Map集合
	private Map<Integer,List<Integer>> adjacencyList;
	
	//初始化
	public GraphSCC(Map<Integer,List<Integer>> adjacencyList){
		this.adjacencyList = adjacencyList;
		//顶点数目
		int vCount = adjacencyList.size();

		List<GraphNode> list = new ArrayList<GraphNode>();	
		for(int i=0;i<vCount;i++){
			list.add(new GraphNode());
		}
		nodes = list.toArray(new GraphNode[0]);
		
		visited = new boolean[vCount];
	}
	
	/**
	 * 对单个强连通分量进行深度优先搜索
	 * @param v 第几个顶点
	 */
	public void DFSTraverse(int v){
		visited[v] = true;
		nodes[_cc].setName(v);
		int tmp = _cc;
		
		//记录当前遍历到的顶点的pre
		nodes[tmp].setPre(_pre_post++);
		_cc++;
				
		//在该顶点的邻接顶点集合里对邻接顶点继续DFS
		List<Integer> vList = adjacencyList.get(v);
		if(vList.size()>0){
			for(int j=0; j<vList.size(); j++){
				int u = vList.get(j);				
				if(!visited[u]){
					DFSTraverse(u);
				}
			}			
		}
		
		//记录当前遍历到的顶点的post
		nodes[tmp].setPost(_pre_post++);
	}
	
	/**
	 * 对所有强连通分量进行深度优先搜索
	 */
	public void dfs(){
		for(int i=0; i<nodes.length; i++){
			if(!visited[i]){
				//该连通分量在nodes集合的开始位置
				int start = _cc;
				//对该连通分量进行深度遍历
				DFSTraverse(i);
				//该连通分量的大小
				int count = _cc - start;
				
				//该连通分量包含的所有顶点
				GraphNode[] newNodes = new GraphNode[count];
				for(int j=0; j<count;j++){
					int v = start+j;
					newNodes[j] = nodes[v];
				}			
				
				scc.add(newNodes);
			}
		}
	}
	
	//scc: Strong Connected Component,强连通分量
	public List<GraphNode[]> getSCC(){		
		return scc;
	}
	
}

测试代码:

	public static void main(String[] args) {
		try(Scanner scanner = new Scanner
				(GraphDAG.class.getClassLoader().getResourceAsStream("tinyDG.txt"));){
			
			//第一行的数字是顶点的数目
			int v = scanner.nextInt();
			//第二行的数字是边的数目
			int e = scanner.nextInt();
			//构造该图的反向图
			GraphReverse graphReverse = new GraphReverse(v, e);				
			
			//读取每条边对应的两个顶点,将边添加到反向图的邻接表里
			for (int i = 0; i < e; i++) {				
				int v1 = scanner.nextInt();
				int v2 = scanner.nextInt();
				graphReverse.addEdgeReverse(v2, v1);
			}

			//对反向图进行DFS
			GraphSCC graphSCC = new GraphSCC(graphReverse.getAdjacencyListReverse());
			graphSCC.dfs();	
			
			//反向图的强连通分量集合
			List<GraphNode[]> cc = graphSCC.getSCC();
			int count = cc.size();
			System.err.println("********** 共有"+count+"个连通分量,分别为: ********** ");
			
			//遍历强连通分量,输出SCC的post值逆序和每个顶点信息
			for(int i=count-1; i>=0; i--){
				GraphNode[] c = cc.get(i);
				System.err.println("第"+(count-i)+"个连通分量:");
				
				//存储该SCC的post值
				int [] postDESC = new int[c.length];
				
				int index = 0;
				for(GraphNode node : c){
					System.err.println(node.getInfo());
					postDESC[index++] = node.getPost();
				}
				//对post值冒泡排序
				postDESC = BubbleSort.sort(postDESC);
				
				System.err.print("反向图中顶点post的逆序表示: ");
				for(int j=postDESC.length-1; j>=0; j--){
					for(GraphNode node : c){
						if(node.getPost() == postDESC[j]){
							int name = node.getName();
							System.err.print(name+" ");
						}
						continue;
					}
				}
				System.err.println("\n");
			}
			
		} 
	}

测试结果:

第五周作业——有向图强连通分量的编程实现_第1张图片


你可能感兴趣的:(有向图,DFS,作业,强连通分量,反向图)