如果学习x课程前必须先学习y课程,学习y课程前必须先学习z课程,学习z课程前必须先学习x课程,那么一定是有问题了,我们就没有办法学习了,因为这三个条件没有办法同时满足。其中这三门课程x,y,z的条件组成了一个环。

python 检查有向图是否有环 检测有向图是否有环_数组


因此,如果我们要使用拓扑排序解决优先级问题,首先得保证图中没有环的存在。

1.1检测有向环的API设计

python 检查有向图是否有环 检测有向图是否有环_python 检查有向图是否有环_02

在API中添加onStack[]布尔数组,索引为图的顶点,当我们深度搜索的时:

1:在如果当前顶点正在搜索,则把对应的onStack数组中的值修改为true,标识进栈;

2:如果当前顶点搜索完毕,则把对应的onStack数组中的值改为false,标识出栈;

3:如果即将要搜索某个顶点,但该顶点已经在栈中,则图中有环;

python 检查有向图是否有环 检测有向图是否有环_搜索_03


python 检查有向图是否有环 检测有向图是否有环_数组_04


代码实现

public class DirectedCycle {
    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;
    //记录图中是否有环
    private boolean hasCycle;
    //索引代表顶点,使用栈的思想,记录当前顶点有没有已经处于正在搜索的有向路径上
    private boolean[] onStack;


    //创建一个检测对象,检测图G中是否有环
    public DirectedCycle(Digraph G){
        //初始化marked数组
        this.marked = new boolean[G.V()];
        //初始化hasCycle数组
        this.hasCycle = false;
        //初始化onStack数组
        this.onStack = new boolean[G.V()];

        //找到图中每一个顶点,让每一个顶点作为入口,调用一次dfs进行搜索
        for(int v = 0;v < G.V(); v++){
            //判断如果当前顶点还没有被搜索过,则调用dfs进行搜索
            if(!marked[v]){
                dfs(G,v);
            }
        }
    }

    /**
     * 基于深度优先搜索,检测图G中是否有环
     * @param G
     * @param v
     */
    private void dfs(Digraph G,int v){
        //把顶点v表示为已搜索
        marked[v] = true;

        //把当前顶点进栈
        onStack[v] = true;

        //进行深度搜索
        for (Integer w : G.adj(v)) {
            //判断如果当前顶点w没有被搜索过,则继续递归调用dfs方法完成深度优先搜索
            if(!marked[w]){
                dfs(G,w);
            }

            //判断当前顶点w是否已经在栈中,如果已经在栈中,证明当前顶点之前处于正在搜索的状态
            if(onStack[w]){
                hasCycle = true;
                return;
            }
        }
    }

    /**
     * 判断有向环中是否有环
     * @return
     */
    public boolean hasCycle(){
        return hasCycle;
    }
}