DFS应用(拓扑排序和强连通分支)
来源:互联网 发布:select linux 编辑:程序博客网 时间:2024/05/16 10:49
本文将用实例分析DFS搜索算法的两大应用:
1、运用深度优先搜索,对一个有向无回路图DAG进行拓扑排序;
2、运用深度优先搜索,将一个有向图分解为各强连通分支。
一、拓扑排序
首先拓扑排序是针对有向无回路图来说的,反之,如果图中有回路,就不可能存在这样的线性序列。 Topological-Sort算法可以产生一个有向无回路图G的拓扑序列。
1、Topological-Sort算法实现思路:
<1>调用DFS去搜索有向无回路图并记录每一个顶点的finishTime,也就是记录在DFS遍历过程中每个顶点的完成时间;
<2>当每个顶点完成时,将其从一个链表的头部插入(开始时简历一个空链表,每完成一个顶点便从链表头部插入);
<3>当DFS遍历结束后,返回这个链表便是有向无回路图的拓扑排序。
Topological-Sort算法的时间复杂度是O(V+E),也就是深度优先搜索的时间复杂度,因为算法中将|V|个顶点插入到链表中所用的时间是O(1)。
后文将给出Topological-Sort算法的Java实例分析!!!
二、强连通分支
有向图G的一个强连通分支就是一个最大的顶点集C ⊆ V,对于C中的每一个顶点 u 和 v,他们之间都是相互可达到的。在得到图的强连通分支时,需要使用到图G的转置,也就是原图G将所有边都反向,便可得到图的转置。使用Strongly-Connected-Comopnents算法,可以正确计算有向图G的强连通分支。
1、Strongly-Connected-Comopnents算法实现思路:
<1>对原图执行DFS算法,并得到每个顶点的finishTime;
<2>计算得到原图的转置;
<3>对原图的转置图在执行DFS算法,但是注意此时在DFS的主循环中按照上面得到的顶点的finishTime的降序依次考虑每个顶点;
<4>输出第<3>步DFS过程中得到的每一棵树便是原图的每一个强连通分支。最后形成的森林便是原图强连通分支的集合。
Strongly-Connected-Comopnents算法的时间复杂度也是O(E+V),便是两次深度优先搜索的时间复杂度。
下面将给出Strongly-Connected-Comopnents算法的Java实例分析!!!
三、拓扑排序和强连通分支的Java实现
我们将对下面的图进行拓扑排序并且生成其强连通分支:
1、Graph类:描述图含有的属性
public class Graph { Vertex[] vertexArray=new Vertex[100]; int verNum=0; int edgeNum=0;}
2、Vertex类:描述图顶点包含的属性
public class Vertex { String verName; String color; int discoverTime; int finishTime; Vertex parent; Vertex nextNode;}
3、VertexNode类:实现链表的节点类
/** * 用于存储顶点对象的链表 * @author King */public class VertexNode { Vertex vertexData; VertexNode next; public VertexNode(Vertex ver){ vertexData=ver; }}
4、LinkList类:该类实现链表的部分操作
public class LinkList { VertexNode first; /** * 构造函数,用于初始化头节点 */ public LinkList(){ this.first=null; } /** * 向空链表中插入头结点 * @param data 头链表数据 */ public void addFirstNode(Vertex vertex){ VertexNode vertexNode=new VertexNode(vertex); vertexNode.next=first; first=vertexNode; } /** * 从链表尾部插入节点 * @param vertex 顶点对象 */ public void addTailNode(Vertex vertex){ VertexNode vertexNode=new VertexNode(vertex); VertexNode current=first; if(current==null){ vertexNode.next=current; first=vertexNode; }else{ VertexNode present=current; while(current!=null){ present=current; current=current.next; } present.next=vertexNode; } }}
5、拓扑排序和强连通分支实现主类
import java.util.ArrayList;import java.util.Scanner;public class TopologicalSort { int time;// 下面链表用于记录拓扑序列 LinkList linkList=new LinkList();// 下面是为了实现反向图而采用的数据结果 ArrayList<String> arrayEdge1=new ArrayList<String>(); ArrayList<String> arrayEdge2=new ArrayList<String>(); /** * 根据用户输入的string类型的顶点返回该顶点 * @param graph 图 * @param str 输入数据 * @return返回一个顶点 */ public Vertex getVertex(Graph graph,String str){ for(int i=0;i<graph.verNum;i++){ if(graph.vertexArray[i].verName.equals(str)){ return graph.vertexArray[i]; } } return null; } /** * 根据用户输入的数据初始化一个图,以邻接表的形式构建! * @param graph 生成的图 */ public void initialGraph(Graph graph){ @SuppressWarnings("resource") Scanner scan=new Scanner(System.in); System.out.println("请输入顶点数和边数:"); graph.verNum=scan.nextInt(); graph.edgeNum=scan.nextInt(); System.out.println("请依次输入定点名称:"); for(int i=0;i<graph.verNum;i++){ Vertex vertex=new Vertex(); String name=scan.next(); vertex.verName=name; vertex.color="white"; vertex.discoverTime=0; vertex.finishTime=0; vertex.parent=null; vertex.nextNode=null; graph.vertexArray[i]=vertex; } System.out.println("请依次输入图的便边:"); for(int i=0;i<graph.edgeNum;i++){ String preV=scan.next(); String folV=scan.next(); arrayEdge1.add(preV); arrayEdge2.add(folV); Vertex v1=getVertex(graph,preV); if(v1==null) System.out.println("输入边存在图中没有的顶点!"); Vertex v2=new Vertex(); v2.verName=folV; v2.nextNode=v1.nextNode; v1.nextNode=v2; } } /** * 输入图的邻接表 * @param graph 待输出的图 */ public void outputGraph(Graph graph){ for(int i=0;i<graph.verNum;i++){ Vertex vertex=graph.vertexArray[i]; System.out.print(vertex.verName); Vertex current=vertex.nextNode; while(current!=null){ System.out.print("-->"+current.verName); current=current.nextNode; } System.out.println(); } } /** *利用图的DFS遍历完成拓扑排序主函数 * @param graph 图 */ public void dfsTopoligical(Graph graph){ for(int i=0;i<graph.verNum;i++){ if(graph.vertexArray[i].color.equals("white")){ topological(graph.vertexArray[i],graph); System.out.println(); } } } /** * 利用DFS完成图的拓扑排序辅助函数 * @param vertex 顶点 * @param graph 图 */ public void topological(Vertex vertex,Graph graph){ vertex.color="gray"; time=time+1; vertex.discoverTime=time; System.out.print(vertex.verName+"-->"); Vertex current=vertex.nextNode; while(current!=null){ Vertex currentNow=getVertex(graph, current.verName); if(currentNow.color.equals("white")) topological(currentNow,graph); current=current.nextNode; } vertex.color="black"; time=time+1; vertex.finishTime=time; linkList.addFirstNode(vertex); } /** * 构建最初输入图的转置,即各边全部变成原来的反向边 * @param graph */ public void reverseGraph(Graph reverseGraph,Graph graph){ reverseGraph.edgeNum=graph.edgeNum; reverseGraph.verNum=graph.verNum; for(int i=0;i<graph.verNum;i++){ graph.vertexArray[i].nextNode=null; graph.vertexArray[i].color="white"; graph.vertexArray[i].discoverTime=0; graph.vertexArray[i].finishTime=0; reverseGraph.vertexArray[i]=graph.vertexArray[i]; } for(int i=0;i<arrayEdge1.size();i++){ String preV=arrayEdge2.get(i); String folV=arrayEdge1.get(i); Vertex v1=getVertex(graph,preV); if(v1==null) System.out.println("输入边存在图中没有的顶点!"); Vertex v2=new Vertex(); v2.verName=folV; v2.nextNode=v1.nextNode; v1.nextNode=v2; } } /** * 此函数用于寻找出图的强连通分支 * @param reverseGraph 原图的转置图 */ public void strongConnectedComponent(Graph reverseGraph){// 和DFS遍历的区别边在于是在DFS的主循环中按照finishTime的降序顺序遍历每个顶点 VertexNode current=linkList.first; while(current!=null){ Vertex ver=getVertex(reverseGraph, current.vertexData.verName); if(ver.color.equals("white")){ topological(ver, reverseGraph); System.out.println(); } current=current.next; } } public static void main(String[] args) { TopologicalSort topologicalSort=new TopologicalSort(); Graph graph=new Graph(); topologicalSort.initialGraph(graph); System.out.println("输出图的邻接链表表示为:"); topologicalSort.outputGraph(graph); System.out.println("\n输出图的DFS遍历结果为:"); topologicalSort.dfsTopoligical(graph); System.out.println("\n图的拓扑排序结果为:"); VertexNode current=topologicalSort.linkList.first; while(current!=null){ System.out.print(current.vertexData.verName+"-->"); current=current.next; } System.out.println("null"); Graph reverseGraph=new Graph(); topologicalSort.reverseGraph(reverseGraph,graph); System.out.println("\n输出原图的转置图的邻接链表表示为:"); topologicalSort.outputGraph(reverseGraph); System.out.println("\n输出该图的强连通分支为:"); topologicalSort.strongConnectedComponent(reverseGraph); }}/*测试数据:顶点(8):v1v2v3v4v5v6v7v8边的集合(13):v1 v2v1 v4v2 v3v2 v6v4 v1v4 v3v5 v1v5 v6v5 v7v6 v2v7 v6v7 v8v8 v5*/
大家可以仔细观察操作的图,和下面的结果进行比对,理解Topological-Sort算法和Strongly-Connected-Comopnents算法的实现思路。代码中写有注释,可以帮助大家理解。
测试输入截图(部分):
拓扑排序结果如下图:
得到的强连通分支如下图所示:
如有问题,欢迎大家指针,谢谢!
- DFS应用(拓扑排序和强连通分支)
- DFS应用:图的拓扑排序以及Kosaraju强连通分量算法
- 【图论】强连通分量和拓扑排序
- (intermediate)DFS (强连通+缩点+拓扑排序) UVA 11098 - Battle II
- 算法系列笔记7(有关图的算法一—搜索,拓扑排序和强连通分支)
- 算法系列笔记7(有关图的算法一—搜索,拓扑排序和强连通分支)
- 图基本算法介绍:广度优先搜索、深度优先搜索、拓扑排序、强连通分支(算法篇)
- 洛谷Oj-信息传递-拓扑排序+DFS/Tarjan强连通分量
- Dominos 拓扑排序 强连通 缩点
- CSU1580: Outing(强连通+拓扑排序+dp)
- COMP2907 图论 强连通判断 拓扑排序
- poj 2762 强连通分量+拓扑排序(判断图是否为单向连通)
- poj 2762(弱连通:强连通+缩点+拓扑排序)
- 有向图中寻找强连通分量(环)和拓扑排序——Kosaraju、Trajan、Gabow算法
- DFS应用——查找强分支
- POJ2186 强连通分支(Strongly_Connected_Components)
- 强连通分支
- POJ_1236 强连通分支
- JAVA WEB从入门到精通 day23 使用java发送邮件
- 优秀Java程序员必须了解的GC工作原理
- BZOJ 2555 SubString LCT 后缀自动机
- 微信小程序上滑加载下拉刷新(onscrollLower)分批加载数据 上篇
- HDU2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
- DFS应用(拓扑排序和强连通分支)
- 文章标题
- 计算机数值方法与算法-拉格朗日插值多项式总结
- SSH_day01:helloWorld
- 软件项目管理工具对比----推进敏捷开发管理工具的使用
- CCF推荐-计算机网络领域顶级期刊会议
- 武大网络赛H题
- unity3d之AI初级将AI角色抽象成一个质点---Vehicle类
- POJ 3270 Cow Sorting (置换群)