拓扑排序

拓扑排序:
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
有向无环图:
执行步骤
由AOV网构造拓扑序列的拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。
(1) 选择一个入度为0的顶点并输出之;
(2) 从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列。

package com.gloomy.graph;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;

/**
 * 拓扑排序 基于DFS的算法,加入结果集的条件是:顶点的出度为0。 基于Kahn算法,加入结果集的条件是:顶点入度为0。
 * 两种算法的思想犹如一枚硬币的两面,看似矛盾,实则不然。一个是从入度的角度来构造结果集,另一个则是从出度的角度来构造。
 * 
 * @author 过路的守望
 *
 */
public class ToplogicalSort {

    /**
     * 顶点类
     * 
     * @author 过路的守望
     *
     */
    private static class Vertex {
        /*
         * @indgree 入度
         */
        private int indgree;
        /*
         * @label 标签
         */
        private String label;
        /*
         * 前继顶点
         */
        private Vertex preVertex;

        /*
         * 记录是否访问过
         */
        private boolean visited;

        /*
         * 记录是否在栈中
         */
        private boolean inStack;

        /*
         * @adjacentlist 邻接表
         */
        private List<Vertex> adjacentList;

        public Vertex(int indgree, String label) {
            this.adjacentList = new ArrayList<Vertex>();
            this.indgree = indgree;
            this.label = label;
        }

        public List<Vertex> getAdjacentList() {
            return adjacentList;
        }

        public int getIndgree() {
            return indgree;
        }

        public String getLabel() {
            return label;
        }

        public Vertex getPreVertex() {
            return preVertex;
        }

        public boolean isInStack() {
            return inStack;
        }

        public boolean isVisited() {
            return visited;
        }

        public void setAdjacentList(List<Vertex> adjacentList) {
            this.adjacentList = adjacentList;
        }

        public void setIndgree(int indgree) {
            this.indgree = indgree;
        }

        public void setInStack(boolean inStack) {
            this.inStack = inStack;
        }

        public void setLabel(String label) {
            this.label = label;
        }

        public void setPreVertex(Vertex preVertex) {
            this.preVertex = preVertex;
        }

        public void setVisited(boolean visited) {
            this.visited = visited;
        }

    }

    public static void main(String[] args) {
        Vertex A = new Vertex(0, "A");
        Vertex B = new Vertex(1, "B");
        Vertex C = new Vertex(2, "C");
        Vertex D = new Vertex(3, "D");
        Vertex E = new Vertex(1, "E");
        Vertex F = new Vertex(3, "F");
        Vertex G = new Vertex(2, "G");
        A.getAdjacentList().add(B);
        A.getAdjacentList().add(C);
        A.getAdjacentList().add(D);
        B.getAdjacentList().add(D);
        B.getAdjacentList().add(E);
        C.getAdjacentList().add(F);
        D.getAdjacentList().add(C);
        D.getAdjacentList().add(F);
        D.getAdjacentList().add(G);
        E.getAdjacentList().add(D);
        E.getAdjacentList().add(G);
        G.getAdjacentList().add(F);
        ToplogicalSort sort = new ToplogicalSort();
        List<Vertex> vertexs = new ArrayList<Vertex>();
        vertexs.add(A);
        vertexs.add(B);
        vertexs.add(C);
        vertexs.add(D);
        vertexs.add(E);
        vertexs.add(F);
        vertexs.add(G);
        /* List<Vertex> pathList = sort.toplogicalSort(vertexs); */
        List<Vertex> pathList = sort.toplogicalSortByRec(vertexs);
        Collections.reverse(pathList);
        if (pathList != null) {
            for (Vertex v : pathList) {
                System.out.print(v.getLabel() + "-->");
            }
        }
    }

    /*
     * 记录拓扑排序的路径
     */
    private List<Vertex> rootList = new ArrayList<Vertex>();
    /*
     * 添加入度为0的顶点
     */
    private Queue<Vertex> queue;

    /*
     * 添加环中的顶点
     */
    private Stack<Vertex> stack;

    /**
     * 判断图是DAG可以使用基于DFS的算法,复杂度为O(E+V),而后面的拓扑排序也是依赖于DFS,复杂度为O(E+V)
     * 因为添加顶点到集合中的时机是在DFS方法即将退出之时
     * ,而DFS方法本身是个递归方法,只要当前顶点还存在边指向其它任何顶点,它就会递归调用DFS方法,
     * 而不会退出。因此,退出DFS方法,意味着当前顶点没有指向其它顶点的边了,即当前顶点是一条路径上的最后一个顶点。
     * 
     * @param vertex
     * @return
     */
    private List<Vertex> DFSToplogicalSort(Vertex vertex) {
        /*
         * 当前顶点已被访问
         */
        vertex.setVisited(true);
        /*
         * 当前顶点在栈中,用来检测是否含环
         */
        vertex.setInStack(true);
        for (Vertex vertex2 : vertex.getAdjacentList()) {
            if (hasCycle()) {
                return null;
            }
            if (!vertex2.isVisited()) {
                /*
                 * 记录前继顶点
                 */
                vertex2.setPreVertex(vertex);
                DFSToplogicalSort(vertex2);
            }
            /*
             * 顶点已被访问过并且在栈中,现在再次被访问,证明存在环
             */
            else if (vertex2.isInStack()) {
                stack = new Stack<ToplogicalSort.Vertex>();
                /*
                 * stack记录环的组成顶点
                 */
                stack.push(vertex2);
                for (Vertex begin = vertex; begin != vertex2; begin = begin
                        .getPreVertex()) {
                    stack.push(begin);
                }
                stack.push(vertex2);

            }

        }
        /*
         * 当前顶点的邻接顶点全部处理完后将当前顶点加入rootList中
         */
        rootList.add(vertex);
        /*
         * 标记当前顶点不在栈中
         */
        vertex.setInStack(false);
        return rootList;

    }

    /*
     * 将所有入度为0的顶点放入一个初始为空的队列中
     */
    public void findNewVertexOfIndgreeZeros(List<Vertex> vertexs) {
        Iterator<Vertex> it = vertexs.iterator();
        while (it.hasNext()) {
            Vertex curVertex = it.next();
            /*
             * 遍历集合时若要删除集合中的元素用迭代器防止抛出异常
             */
            if (curVertex.getIndgree() == 0) {
                queue.offer(curVertex);
                it.remove();
            }
        }
        return;
    }

    /*
     * 判断有无环路
     */
    public boolean hasCycle() {
        return null != stack;
    }

    /*
     * 拓扑排序顺序不一定是唯一的
     */
    public List<Vertex> toplogicalSort(List<Vertex> vertexs) {
        rootList = new ArrayList<Vertex>();
        /*
         * 使用一个队列可以不为Vertex设置是否访问了的属性。因为遍历时一个元素从队列中删除后不会再次加入进队列
         */
        queue = new LinkedList<Vertex>();
        /*
         * 将所有入度为0的顶点放入一个初始为空的队列中
         */
        findNewVertexOfIndgreeZeros(vertexs);
        Vertex curVertex = null;
        while (!queue.isEmpty()) {
            /*
             * 当队列不为空时,删除一个顶点v,并将与v邻接的所有顶点入度均减一。
             * 只要有一个顶点的入度为0,就把该顶点放入队列中。此时,拓扑排序的顺序就是 顶点出队的顺序。
             */
            curVertex = queue.poll();
            vertexs.remove(curVertex);
            rootList.add(curVertex);
            for (Vertex adjacentVertex : curVertex.getAdjacentList()) {
                adjacentVertex.setIndgree(adjacentVertex.getIndgree() - 1);
                /*
                 * 将入度为0的顶点加入到队列
                 */
                if (adjacentVertex.getIndgree() == 0) {
                    queue.offer(adjacentVertex);
                }
            }
        }
        /*
         * 如果存在环,则vertexs中的顶点未清除完
         */
        if (!vertexs.isEmpty()) {
            System.out.println("存在环!");
            return null;
        }
        return rootList;
    }

    /**
     * 基于深度优先遍历递归实现拓扑排序
     * 
     * @param vertexs
     * @return
     */
    public List<Vertex> toplogicalSortByRec(List<Vertex> vertexs) {
        for (Vertex vertex : vertexs) {
            if (!vertex.isVisited()) {
                DFSToplogicalSort(vertex);
            }
        }
        return rootList;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值