1.1 有向图
定义:边不仅仅连接两个顶点,并且具有方向。
和无向图相比,有向图除了边具有方向以外,并无多大不同,因此代码的实现也相差无几。仅仅多了一个反向图,用以得到指向v的其他顶点,并用队列存储。
反向图
public Queue<Integer> adj(int v){
return adj[v];
}
//该图的反向图
public Digraph reverse(){
//创建有向图对象
Digraph r = new Digraph(V);
for (int v=0;v<V;v++){
//遍历v的邻接表
for (Integer w : adj[v]) {
r.addEdge(w,v);
}
}
return r;
}
有向图完整代码
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 getV(){
return V;
}
//获取边的数目
public int getE(){
return E;
}
//向图中添加一条边
public void addEdge(int v,int w){
//只需要让w出现在v的邻接表中,因为边是有方向的
adj[v].enqueue(w);
//边的数量加一
E++;
}
//获取和顶点v相邻的所有顶点
public Queue<Integer> adj(int v){
return adj[v];
}
//该图的反向图
public Digraph reverse(){
//创建有向图对象
Digraph r = new Digraph(V);
for (int v=0;v<V;v++){
//遍历v的邻接表
for (Integer w : adj[v]) {
r.addEdge(w,v);
}
}
return r;
}
}
1.2 拓扑排序
定义:给定一幅有向图,将所有的顶点排序,使得所有的有向边都从排在前边的元素指向后边的元素,此刻就可以得出每个元素的优先级。 如图所示:
有向图
经过拓扑排序的有向图
如果要使用拓扑排序,要保证图中没有环存在。因此先创建一个类来判断图中有没有环。
1.2.1 检测图中是否有环存在
构造方法
//索引代表顶点,值代表当前顶点是否被搜索过
private boolean[] marked;
private boolean hasCycle;
//索引代表值,值代表当前顶点有没有处在正在搜索的有向路径上
private boolean[] onStack;
public DirectedCycle(Digraph G){
this.marked=new boolean[G.getV()];
this.hasCycle=false;
this.onStack=new boolean[G.getV()];
//找到图中每一个顶点,让每一个顶点作为入口,调用dfs进行搜索,这是为了避免非连通图的出现
for (int v=0;v<G.getV();v++){
//如果当前顶点没有被搜索过,则调用dfs
if (!marked[v]){
dfs(G,v);
}
}
}
通过深度优先搜索,检测图G是否有环
public void dfs(Digraph G,int v){
//把顶点v标识为已搜索
marked[v]=true;
//把当前顶点进栈
onStack[v]=true;
//进行深度优先搜索
for (Integer w : G.adj(v)) {
//如果当前顶点没有被搜索过,则递归调用dfs
if (!marked[w]){
dfs(G,w);
}
if (onStack[w]){
hasCycle=true;
return;
}
}
//把当前顶点出栈
onStack[v]=false;
}
检测有向环完整代码
public class DirectedCycle {
//索引代表顶点,值代表当前顶点是否被搜索过
private boolean[] marked;
private boolean hasCycle;
//索引代表值,值代表当前顶点有没有处在正在搜索的有向路径上
private boolean[] onStack;
public DirectedCycle(Digraph G){
this.marked=new boolean[G.getV()];
this.hasCycle=false;
this.onStack=new boolean[G.getV()];
//找到图中每一个顶点,让每一个顶点作为入口,调用dfs进行搜索,这是为了避免非连通图的出现
for (int v=0;v<G.getV();v++){
//如果当前顶点没有被搜索过,则调用dfs
if (!marked[v]){
dfs(G,v);
}
}
}
//通过深度优先搜索,检测图G是否有环
public void dfs(Digraph G,int v){
//把顶点v标识为已搜索
marked[v]=true;
//把当前顶点进栈
onStack[v]=true;
//进行深度优先搜索
for (Integer w : G.adj(v)) {
//如果当前顶点没有被搜索过,则递归调用dfs
if (!marked[w]){
dfs(G,w);
}
if (onStack[w]){
hasCycle=true;
return;
}
}
//把当前顶点出栈
onStack[v]=false;
}
//判断w顶点与s顶点是否有环
public boolean hasCycle(){
return hasCycle;
}
}
1.2.2 基于深度优先的顶点排序
实现顶点排序的关键是我们需添加一个栈来存储顶点,在进行深度优先搜索时,每搜索完一个顶点,就放入栈中,这样就实现了顶点排序。如图所示:
构造方法
private boolean[] marked;
//使用栈,存储顶点序列
private Stack<Integer> reversepost;
//创建一个检测环对象,判断图中是否有环
public DepthFirstOrder(Digraph G){
this.marked=new boolean[G.getV()];
this.reversepost=new Stack<Integer>();
//找到图中每一个顶点,让每一个顶点作为入口,调用dfs进行搜索,这是为了避免非连通图的出现
for (int v=0;v<G.getV();v++){
//如果当前顶点没有被搜索过,则调用dfs
if (!marked[v]){
dfs(G,v);
}
}
}
把顶点进行排序
public void dfs(Digraph G,int v){
//把顶点v标识为已搜索
marked[v]=true;
//进行深度优先搜索
for (Integer w : G.adj(v)) {
//如果当前顶点没有被搜索过,则递归调用dfs
if (!marked[w]) {
dfs(G, w);
}
}
//让顶点v进栈
reversepost.push(v);
}
顶点排序完整代码
public class DepthFirstOrder {
private boolean[] marked;
//使用栈,存储顶点序列
private Stack<Integer> reversepost;
//创建一个检测环对象,判断图中是否有环
public DepthFirstOrder(Digraph G){
this.marked=new boolean[G.getV()];
this.reversepost=new Stack<Integer>();
//找到图中每一个顶点,让每一个顶点作为入口,调用dfs进行搜索,这是为了避免非连通图的出现
for (int v=0;v<G.getV();v++){
//如果当前顶点没有被搜索过,则调用dfs
if (!marked[v]){
dfs(G,v);
}
}
}
//把顶点进行排序
public void dfs(Digraph G,int v){
//把顶点v标识为已搜索
marked[v]=true;
//进行深度优先搜索
for (Integer w : G.adj(v)) {
//如果当前顶点没有被搜索过,则递归调用dfs
if (!marked[w]) {
dfs(G, w);
}
}
//让顶点v进栈
reversepost.push(v);
}
//获取顶点线性序列
public Stack<Integer> getReversepost(){
return reversepost;
}
}
上述两个类完成之后,在进行拓扑排序就非常简单了,只需先判断图中有没有环,在进行顶点排序即可。
拓扑排序完整代码
public class TopoLogical {
private Stack<Integer> order;
public TopoLogical(Digraph G){
//创建一个检测有向环的对象
DirectedCycle cycle = new DirectedCycle(G);
//判断图G中有没有环,如果没有,则进行顶点排序
if (!cycle.hasCycle()){
DepthFirstOrder firstOrder = new DepthFirstOrder(G);
order=firstOrder.getReversepost();
}
}
//判断图G是否有环
public boolean isCycle(){
return order==null;
}
//获取拓扑排序的所有顶点
public Stack<Integer> order(){
return order;
}
}
b站详细讲解网址:http://yun.itheima.com/course/639.html