有向图是一副具有方向性的图,是由一组顶点和一组有方向的边组成的,每条方向的边都连着一对有序的顶点
类名 | Digraph |
---|---|
构造方法 | Digraph(int V):创建一个包含V个顶点但不包含边的有向图 |
成员方法 | 1.public int V():获取图中顶点的数量 2.public int E():获取图中边的数量 3.public void addEdge(int v,int w):向有向图中添加一条边 v->w 4.public Queue adj(int v):获取由v指出的边所连接的所有顶点 5.private Digraph reverse():该图的反向图 |
成员变量 | 1.private final int V: 记录顶点数量 2.private int E: 记录边数量 3.private Queue[] adj: 邻接表 |
/** * 有向图 * @date 2021/7/9 16:54 */ public class Digraph { // 记录顶点数量 private final int V; // 记录边数量 private int E; // 邻接表 private Queue<Integer>[] adj; public Digraph(int V) { //初始化顶点数量 this.V = V; //初始化边的数量 this.E = 0; //初始化邻接表 this.adj = new Queue[V]; //初始化邻接表中的空队列 for (int i = 0; i < adj.length; i++) { adj[i] = new Queue<Integer>(); } } // 获取顶点数目 public int V() { return V; } // 获取边的数目 public int E() { return E; } // 向有向图中添加一条边v->w public void addEdge(int v, int w) { // 只需要让顶点w出现在顶点v的邻接表中,因为边是有方向的,最终,顶点v的邻接表中存储的相邻顶点的含义是: v->其他顶点 adj[v].enqueue(w); E++; } // 获取由v指出的边所连接的所有顶点 public Queue<Integer> adj(int v) { return adj[v]; } // 该图的反向图 private Digraph reverse() { // 创建有向图对象 Digraph r = new Digraph(V); // 遍历原图的每一个节点 for (int v = 0; v < V; v++) { // 获取由该顶点v指出的所有边 for (Integer w : adj[v]) { // 原图中表示的是由顶点v->w的边 r.addEdge(w,v); // w->v } } return r; } }
// 测试有向图数据结构 public static void test01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Digraph digraph = new Digraph(10); digraph.addEdge(1,2); digraph.addEdge(1,3); digraph.addEdge(4,1); System.out.print("由顶点1指出的边所连接的所有顶点:"); for (Integer w : digraph.adj(1)) { System.out.print(w+"顶点 "); } System.out.println(); // 反向该图 Class<Digraph> digraphClass = Digraph.class; Method method = digraphClass.getDeclaredMethod("reverse", null); method.setAccessible(true); digraph = (Digraph)method.invoke(digraph); System.out.println("----------------------反向该图后---------------------"); System.out.print("由顶点1指出的边所连接的所有顶点:"); for (Integer w : digraph.adj(1)) { System.out.print(w+"顶点 "); } }
类名 | DirectedCycle |
---|---|
构造方法 | DirectedCycle(Digraph G):创建一个检测环对象,检测图G中是否有环 |
成员方法 | 1.private void dfs(Digraph G,int v):基于深度优先搜索,检测图G中是否有环 2.public boolean hasCycle():判断图中是否有环 |
成员变量 | 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索 2.private boolean hasCycle: 记录图中是否有环 3.private boolean[] onStack:索引代表顶点,使用栈的思想,记录当前顶点有没有已经处于正在搜索的有向路径上 |
/** * 检测有向图中的环 * * @date 2021/7/9 20:41 */ public class DirectedCycle { //索引代表顶点,值表示当前顶点是否已经被搜索 private boolean[] marked; //记录图中是否有环 private boolean hasCycle; //索引代表顶点,使用栈的思想,记录当前顶点有没有已经处于正在搜索的有向路径上 private boolean[] onStack; //创建一个检测环对象,检测图G中是否有环 public DirectedCycle(Digraph G) { //创建一个和图的顶点数一样大小的marked数组 marked = new boolean[G.V()]; //创建一个和图的顶点数一样大小的onStack数组 onStack = new boolean[G.V()]; //默认没有环 this.hasCycle = false; //遍历搜索图中的每一个顶点 for (int v = 0; v < G.V(); v++) { //如果当前顶点没有搜索过,则搜索 if (!marked[v]) { dfs(G, v); } } } //基于深度优先搜索,检测图G中是否有环 private void dfs(Digraph G, int v) { //把当前顶点标记为已搜索 marked[v] = true; //让当前顶点进栈 onStack[v] = true; //遍历v顶点的邻接表,得到每一个顶点w for (Integer w : G.adj(v)) { //如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点 if (!marked[w]) { dfs(G, w); } //如果顶点w已经被搜索过,则查看顶点w是否在栈中,如果在,则证明图中有环,修改hasCycle标 记,结束循环 if (onStack[w]) { hasCycle = true; return; } } //当前顶点已经搜索完毕,让当前顶点出栈 onStack[v]=false; } //判断w顶点与s顶点是否相通 public boolean hasCycle() { return hasCycle; } }
public class DirectedCycleTest { public static void main(String[] args) { Digraph digraph = new Digraph(10); digraph.addEdge(1,2); digraph.addEdge(2,3); digraph.addEdge(3,1); DirectedCycle directedCycle = new DirectedCycle(digraph); System.out.println("是否有环:" + directedCycle.hasCycle()); } }
类名 | DepthFirstOrder |
---|---|
构造方法 | DepthFirstOrder(Digraph G):创建一个顶点排序对象,生成顶点线性序列; |
成员方法 | 1.private void dfs(Digraph G,int v):基于深度优先搜索,生成顶点线性序列 2.public Stack reversePost():获取顶点线性序列 |
成员变量 | 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索 2.private Stack reversePost: 使用栈,存储顶点序列 |
/** * 基于深度优先的顶点排序 * * @date 2021/7/9 21:15 */ public class DepthFirstOrder { // 索引代表顶点,值代表当前顶点是否已经被搜索 private boolean[] marked; // 使用栈,存储顶点序列 private Stack<Integer> reversePost; // 创建一个检测环对象,检测图G中是否有环 public DepthFirstOrder(Digraph G) { // 初始化mrked数组 marked = new boolean[G.V()]; // 初始化reversePost栈 reversePost = new Stack<Integer>(); // 遍历图中的每一个顶点,让每一个顶点作为入口,完成一次深度优先搜索 for (int v = 0; v < G.V(); v++) { if (!marked[v]) { dfs(G, v); } } } // 基于深度优先搜索,检测图G中是否有环 private void dfs(Digraph G, int v) { // 标记当前v已经被搜索 marked[v] = true; // 通过循环深度搜索顶点v for (Integer w : G.adj(v)) { //如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点 if (!marked[w]) { dfs(G, w); } } // 让顶点v进栈 reversePost.push(v); } // 获取顶点线性序列 public Stack<Integer> reversePost() { return reversePost; } }
public class DepthFirstOrderTest { public static void main(String[] args) { Digraph digraph = new Digraph(10); digraph.addEdge(1,2); digraph.addEdge(1,3); DepthFirstOrder depthFirstOrder = new DepthFirstOrder(digraph); Stack<Integer> reversePost = depthFirstOrder.reversePost(); System.out.println("顶点排序序列:"); for (Integer integer : reversePost) { System.out.print(integer + " "); } } }
类名 | TopoLogical |
---|---|
构造方法 | TopoLogical(Digraph G):构造拓扑排序对象 |
成员方法 | 1.public boolean isCycle():判断图G是否有环 2.public Stack order():获取拓扑排序的所有顶点 |
成员变量 | 1.private Stack order: 顶点的拓扑排序 |
/** * 拓扑排序 * @date 2021/7/9 22:34 */ public class TopoLogical { // 顶点的拓扑排序 private Stack<Integer> order; // 构造拓扑排序对象 public TopoLogical(Digraph G) { //创建检测环对象,检测图G中是否有环 DirectedCycle dCycle = new DirectedCycle(G); if (!dCycle.hasCycle()) { //如果没有环,创建顶点排序对象,进行顶点排序 DepthFirstOrder depthFirstOrder = new DepthFirstOrder(G); order = depthFirstOrder.reversePost(); } } // 判断图G是否有环 public boolean isCycle() { return order == null; } // 获取拓扑排序的所有顶点 public Stack<Integer> order() { return order; } }
public class TopoLogicalTest { public static void main(String[] args) { Digraph digraph = new Digraph(10); digraph.addEdge(1,2); digraph.addEdge(1,3); TopoLogical topoLogical = new TopoLogical(digraph); Stack<Integer> order = topoLogical.order(); System.out.print("拓扑排序:"); for (Integer w : order) { System.out.print(w+" "); } } }