有向图2----可达性分析,单点有向路径以及有向环检测

利用DFS,BFS来遍历搜索图,实现以下功能

可达性分析

单点有向路径

单点最短有向路径

有向环检测


可达性分析

给定有向图,判断某一个顶点是否可以到达另一个顶点,或者从一个顶点集合出发是否可以到达另一个顶点。单点可达性以及多点可达性问题,其中多点可达性可以用来解决java  GC中存活对象的判断。

利用marked来标记dfs过程中访问的顶点坐标,则dfs完成时,marked数组中为true的顶点表示可达。

//有向图  dfs 可达性分析
public class DirectedDFS {
	
	private boolean[] marked;
	
	//单点可达性
	//从起点s出发到某个顶点是否可达
	//dfs之后 marked[true]顶点表示可达
	public DirectedDFS(Digraph g, int s){
		marked = new boolean[g.V()];
		dfs(g, s);
	}
	
	//多点可达性
	//从集合sources出发到某个顶点是否可达
	//GC 判断对象是否存活
	public DirectedDFS(Digraph g, Iterable<Integer> sources){
		marked = new boolean[g.V()];
		for(Integer s : sources){
			if(!marked[s]){
				dfs(g, s);
			}
		}
	}
	
	private void dfs(Digraph g, int v) {
		marked[v] = true;
		for(int w : g.adj(v)){
			if(!marked[w])
				dfs(g, w);
		}	
	}
	
	public static void main(String[] args) {
		Digraph g = GraphUtils.getDiGraphPointG1().getG();
		System.out.println(g.toString());
		DirectedDFS dfs = new DirectedDFS(g, 9);
		System.out.println(Arrays.toString(dfs.marked));
		Bag<Integer> b = new Bag<>();
		b.add(0);
		b.add(9);
		DirectedDFS ddfs = new DirectedDFS(g, b);
		System.out.println(Arrays.toString(ddfs.marked));
	}

}

单点有向路径

给定一个有向图,求出从某一个顶点到其他顶点的单点有向路径

和无向图中路径一样,利用dfs来搜索图,利用marked数组来保存访问顶点,利用edgeTo数组来表示有向路径,edgeTo[v]=w表示由w可以到达v。

//有向图  深度优先搜索  单点有向路径
public class DepthFirstDirectedPaths {
	
	private boolean[] marked;
	//edgeTo[w]=v的含义是由v可以直接到达w
	private int[] edgeTo;
	private final int s;
	
	public DepthFirstDirectedPaths(Digraph g, int s){
		this.marked = new boolean[g.V()];
		this.edgeTo = new int[g.V()];
		this.s = s;
		dfs(g, s);
	}
	
	private void dfs(Digraph g, int v) {
		marked[v] = true;
		for(int w : g.adj(v)){
			if(!marked[w]){
				//向v下一个指向w
				//edgeTo[x]得到谁指向x
				edgeTo[w] = v;
				dfs(g, w);
			}
		}
	}

	//起点s到节点v是否存在单点路径
	public boolean hasPathTo(int v){
		return marked[v];
	}
	
	//返回s-->v路径
	public Iterable<Integer> pathTo(int v){
		if(!hasPathTo(v)) return null;
		//利用栈倒序存储
		Stack<Integer> s = new Stack<>();
		//由最终节点向起始节点寻找  edgeTo[x]表示谁指向x
		for(int x=v; x!=this.s; x = edgeTo[x]){
			s.push(x);
		}
		s.push(this.s);
		return s;
	}
	
	public List<Integer> pathTo2(int v){
		if(!hasPathTo(v)) return null;
		//利用栈倒序存储
		Stack<Integer> s = new Stack<>();
		//由最终节点向起始节点寻找  edgeTo[x]表示谁指向x
		for(int x=v; x!=this.s; x = edgeTo[x]){
			s.push(x);
		}
		s.push(this.s);
		List<Integer> l = new ArrayList<>();
		while(!s.isEmpty()){
			l.add(s.pop());
		}
		return l;
	}
	
	public String pathTo3(int v){
		if(!hasPathTo(v)) return null;
		List<Integer> l = pathTo2(v);
		StringBuilder sb = new StringBuilder();
		sb.append(s + ":" + v + "    ");
		for(int i=0; i<l.size(); i++){
			sb.append(l.get(i));
			if(i!=l.size()-1){
				sb.append("-->");
			}
		}
		return sb.toString();
	}
	
	public static void main(String[] args) {

		Digraph g = GraphUtils.getDiGraphPointG1().getG();
		System.out.println(g.toString());
		DepthFirstDirectedPaths dfp = new DepthFirstDirectedPaths(g, 0);
		System.out.println("x" + " " + "edgeTo[x]");
		for(int i=0; i<dfp.edgeTo.length; i++){
			System.out.println(i + "     " + dfp.edgeTo[i]);
		}
		System.out.println(Arrays.toString(dfp.marked));
		System.out.println(dfp.pathTo3(4));
	}

}

单点最短有向路径

dfs可以解决单点有向路径,单最终得到的路径不一定是最短的,可以利用bfs来得到单点最短有向路径。

利用队列来保存顶点,起始顶点入队,访问该顶点的每一个未被访问过的相邻顶点,访问完成,标记该顶点,并且将该顶点入队。

//广度优先搜索  有向图  单点最短有向路径
public class BreadthFirstDirectedPaths {
	
	private boolean[] marked;
	private int[] edgeTo;
	private final int s;
	
	public BreadthFirstDirectedPaths(Digraph g, int s){
		marked = new boolean[g.V()];
		edgeTo = new int[g.V()];
		this.s = s;
		bfs(g, s);
	}

	//bfs按照距离顶点s距离为0,1,...依次来遍历图
	//距离顶点s越近的顶点v越早进入队列,并且标记v已访问,记录s--v路径edgeTo[v]=s;
	//并且之后由于标记过,不会再次访问,edgeTo不会更新,即bfs之后edgeTo中记录最短路径
	private void bfs(Digraph g, int s) {
		Queue<Integer> q = new LinkedList<>();
		marked[s] = true;
		q.add(s);
		while(!q.isEmpty()){
			int v = q.poll();
			for(int w : g.adj(v)){
				if(!marked[w]){
					edgeTo[w] = v;
					marked[w] = true;
					q.add(w);
				}
			}
		}	
	}
	
	//起点s到节点v是否存在单点路径
	public boolean hasPathTo(int v){
		return marked[v];
	}
	
	//返回s-->v路径
	public Iterable<Integer> pathTo(int v){
		if(!hasPathTo(v)) return null;
		//利用栈倒序存储
		Stack<Integer> s = new Stack<>();
		//由最终节点向起始节点寻找  edgeTo[x]表示谁指向x
		for(int x=v; x!=this.s; x = edgeTo[x]){
			s.push(x);
		}
		s.push(this.s);
		return s;
	}
	
	public List<Integer> pathTo2(int v){
		if(!hasPathTo(v)) return null;
		//利用栈倒序存储
		Stack<Integer> s = new Stack<>();
		//由最终节点向起始节点寻找  edgeTo[x]表示谁指向x
		for(int x=v; x!=this.s; x = edgeTo[x]){
			s.push(x);
		}
		s.push(this.s);
		List<Integer> l = new ArrayList<>();
		while(!s.isEmpty()){
			l.add(s.pop());
		}
		return l;
	}
	
	public String pathTo3(int v){
		if(!hasPathTo(v)) return null;
		List<Integer> l = pathTo2(v);
		StringBuilder sb = new StringBuilder();
		sb.append(s + ":" + v + "    ");
		for(int i=0; i<l.size(); i++){
			sb.append(l.get(i));
			if(i!=l.size()-1){
				sb.append("-->");
			}
		}
		return sb.toString();
	}
	
	
	
	public static void main(String[] args) {
		Digraph g = GraphUtils.getDiGraphPointG1().getG();
		System.out.println(g.toString());
		BreadthFirstDirectedPaths bfp = new BreadthFirstDirectedPaths(g, 0);
		System.out.println("x" + " " + "edgeTo[x]");
		for(int i=0; i<bfp.edgeTo.length; i++){
			System.out.println(i + "     " + bfp.edgeTo[i]);
		}
		System.out.println(Arrays.toString(bfp.marked));
		System.out.println(bfp.pathTo3(4));
	}
	
	/*
	 * 图搜索算法思路
	 * 将起点加入数据结构中,重复一下步骤直到数据结构为空
	 * 	      取出数据结构中的数据,标记数据
	 *    将与该数据所有相邻未标记节点加入到数据结构中
	 * 深度优先数据结构使用栈  每次取出最近的节点
	 * 广度优先使用队列,每次取出最远的节点
	 * */

}

有向环检测

给定一个有向图,判断有向图中,是否含有有向环。

利用dfs来从每一个未被访问的顶点作为起始点来进行递归,利用堆栈数组来保存从当前起始点开始,依次访问的顶点顺序,即每递归调用dfs则说明搜索到一个新的节点,判断该节点是否被访问过,如果没有被访问则标记该节点,并且记录在堆栈数组中;如果某个顶点被访问过则判断是否在堆栈数组中,如果存在则说明存在有向环。

//有向环检测   dfs
public class DirectedCycle {
	
	//记录是否访问过
	private boolean[] marked;
	//有向路径保存
	private int[] edgeTo;
	//记录是否在当前栈中
	private boolean[] onStack;
	//记录最终的环节点
	private Stack<Integer> cycle;
	
	public DirectedCycle(Digraph g){
		marked = new boolean[g.V()];
		onStack = new boolean[g.V()];
		edgeTo = new int[g.V()];
		for(int s=0; s<g.V(); s++){
			if(!marked[s])
				dfs(g, s);
		}
	}
	
	private void dfs(Digraph g, int v) {
		//标记当前节点入栈
		onStack[v] = true;
		//标记已经访问
		marked[v] = true;
		for(int w : g.adj(v)){
			if(hasCycle()) return;
			//如果w没有被访问过  记录v-->w路径  对于w继续进行dfs  w入栈
			if(!marked[w]){
				edgeTo[w] = v;
				dfs(g, w);
			}
			//如果w已经出现在栈中,说明出现了环
			//出现有向环   记录该有向环路径
			else if(onStack[w]){
				cycle = new Stack<>();
				for(int x=v; x!=w; x=edgeTo[x]){
					cycle.push(x);
				}
				cycle.push(w);
				cycle.push(v);
			}
		}
		//w出栈
		onStack[v] = false;
	}

	public boolean hasCycle(){
		return cycle != null;
	}
	
	public Iterable<Integer> cycle(){
		return this.cycle;
	}
	
	public static void main(String[] args) {
		Digraph g = GraphUtils.getDiGraphPointG1().getG();
		System.out.println(g.toString());
		DirectedCycle dc  = new DirectedCycle(g);
		System.out.println(dc.cycle());
	}

}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值