图的相关算法2
有向图
在有向图中,边是单向的,每条边所链接的两个顶点都是一个有序堆,它们的邻接性是单向的,许多应用(网络,任务调度,电话)的图都是天然的有向图;
定义:一副有方向性的图,是由一组顶点和一组有方向的边组成,每条有方向的边都连接着有序的一对顶点;
概念
1. 出度:由该顶点指出的边的总数
2. 入度:指向顶点的边的总数;
有向图的数据结构
public class Digraph {
private final int V;
private int E;
private Bag<Integer>[] adj;
public Digraph(int v) {
this.V = v;
this.E = 0;
adj = (Bag<Integer>[]) new Bag[v];
for (int i = 0; i < v; i++) {
adj[i] = new Bag<Integer>();
}
}
public void addEdge(int v, int w) {
adj[v].add(w);//单向
E++;
}
public Iterable<Integer> adj(int v) {
return adj[v];
}
}
有向图取反;
public Digraph reverse() {
Digraph R = new Digraph(V);
for (int i = 0; i < this.V; i++) {
for (int w : this.adj(i)) {
R.addEdge(w, i);
}
}
return R;
}
有向图的可达性
问题:
- 单点可达性:给定一副有向图和一个起点s,回到是否存在一条从s到达给定顶点v的有向路径?
- 多点可达性:给定一副有向图和顶点集合,是否存在一条从集合中的任意顶点到达给定顶点v的有向路径?
其实这些问题比相应的无向图算法的轨迹稍稍简单些,因为深度优先搜索本质上是一种适用于处理有向图的算法
深入理解下述代码有助于巩固对有向图中深度优先搜索的理解;
public class DirectedDFS {
private boolean[] marked;
public DirectedDFS(Digraph G, int s){
marked = new boolean[G.V()];
dfs(G,s);
}
public DirectedDFS(Digraph G , Iterable<Integer> sources) {
this.marked = new boolean[G.V()];
for (Integer s : sources) {
if (!marked[s]){
dfs(G,s);
}
}
}
private void dfs(Digraph g, int s) {
marked[s] = true;
for (int w : g.adj(s)) {
if (!marked[w]) dfs(g,w);
}
}
public boolean isReached(int v){
return marked[v];
}
}
最后只需要判断
DirectedDFS dd= new DirectedDFS(g,s);
dd.isReached(v);//判断是否到达顶点v;
环和有向图
在有向图任务调度问题中,可能存在这种情况,如 任务x必须在任务y之前完成,任务y必须在任务z之前完成,任务z又必须在任务x之前完成,这三个限制条件不可能被同时满足;即:如果一个有优先级的限制的问题中存在环,那么这个问题是无解的;故而需要解决下面这个问题:
有向环的检测。给定的有向图中是否存在有向环?
修改上述代码
public class DirectedDFS {
private boolean[] marked;
private int[] edgeTo;
private Stack<Integer> cycle;
private boolean[] onStack;
public DirectedDFS(Digraph G, int s){
onStack = new boolean[G.V()];
marked = new boolean[G.V()];
edgeTo = new int[G.V()];
dfs(G,s);
}
public DirectedDFS(Digraph G , Iterable<Integer> sources) {
this.marked = new boolean[G.V()];
for (Integer s : sources) {
if (!marked[s]){
dfs(G,s);
}
}
}
private void dfs(Digraph g, int s) {
marked[s] = true;
onStack[s] = true;
for (int w : g.adj(s)) {
if (this.hasCycle()) return;
else if (!marked[w]) {
edgeTo[w] = s;
dfs(g,w);
}
else if (onStack[w]) {
cycle = new Stack<Integer>();
for (int x = s; x != w ; x=edgeTo[x]) {
cycle.push(x);
}
cycle.push(s);
cycle.push(w);
}
onStack[s]=false;
}
}
private boolean hasCycle() {
return cycle!=null;
}
}
在dfs中添加一个onStack[]来保存递归调用期间栈上的所有顶点,当它找到一个边上v-》w,且w在栈中,它就找到了一个有向环,环上的所有顶点可以通过edgeTo中的链接得到;