图的拓扑排序:检测有向环

一.概述

拓扑排序:
给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素,此时就可以明确的表示出每个顶点的优先级。 最终得到一个有序序列。
在这里插入图片描述

如果要使用拓扑排序解决优先级问题,需要先保证有向图中没有环!

API:
在这里插入图片描述

检测有向环的过程:

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

  1. 在如果当前顶点正在搜索,则把对应的onStack数组中的值改为true,标识进栈;
  2. 如果当前顶点搜索完毕,则把对应的onStack数组中的值改为false,标识出栈;
  3. 如果即将要搜索某个顶点,但该顶点已经在栈中,则图中有环;

关于onStack ! ※
当顶点已经被搜索过即marked[v]=true时,常规dfs到此就是递归结束了,而在这里将进入判断,如果之前在inStack中为true,代表该顶点已经进入了搜索路径,再遇到即为存在环!
遍历完后,所有经过dfs遍历的顶点要onStack弹出栈!因为不同的搜索路径可能会有重复的顶点!要将onStack里的元素还原为false,再从新的顶点出发进行搜索。

在这里插入图片描述
从1出发搜索到3, 0出发也会搜索到3! onStack要出栈!

二.实现

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

    public DirectCycle(Digraph g) {
        this.marked=new boolean[g.V()];
        this.hasCycle=false;
        this.onStack=new boolean[g.V()];

        //找到图中每一个顶点!让每一个顶点都作为入口去用dfs搜索!
        for (int i = 0; i < g.V(); i++) {
            if (!marked[i]) { //如果没有搜索过则使用dfs进行搜索。搜索过就不用dfs了 !

                dfs(g, i);
            }
        }
    }

    private void dfs(Digraph g,int v){ //还是用dfs深度优先来搜索
        marked[v]=true;   //标记v为已搜索

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




        //dfs搜索, 并检查是否存在环!
        for (Integer k : g.adj(v)) { //遍历当前顶点的邻接表
            if (!marked[k]) { //判断视为递归出口
                dfs(g, k); //没有被搜索过则继续递归dfs
            } else {  
        // marked和ontack看似一样,但是,不同的搜索路径可能会有重复的顶点,所以必须用onStack来记录每一次的路径上的点然后顶点出栈将onStack清空!

        //当邻接表中的顶点已经被搜索过时,走到这一步, 普通dfs中会到此就已经递归结束了。
        //判断当前顶点是否在栈中,如果是则表示该顶点处于正在搜索的状态,即存在环;否则出栈
                if (onStack[k]) { //如果栈中存在,则判断为存在环
                    hasCycle = true;
                    return; //有环则直接结束。
                }
            }
        }

        // 让onStack还原为全false
        onStack[v]=false;
    }

    public boolean hasCycle(){
        return hasCycle;
    }

    private boolean[] onStack(){//索引代表顶点,使用栈的思想,记录当前顶点有没有处于搜索路径上!
        return onStack;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值