图——有向图,拓扑排序

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

猜你喜欢

转载自blog.csdn.net/love521314123/article/details/107599347
今日推荐