拓扑排序

拓扑排序:

概述:

​ 一个概念的诞生离不开实际问题的提出。大学有很多门课,其中一些课刚上大学就可以学,有一些需要一些先修课,我们要怎么安排上课的顺序来保证上课的效率。如何解决这个实际问题,就引出了拓扑排序

​ 拓扑排序是对有向无环图进行排序的一种算法。有向:显而易见,我们的先修关系是需要有方向的,所以图一定要是有向的。无环:如果两门课互为先修课,那么该问题就无解。所以要保证这个图的无环的。

​ 具体如何对图进行排序,我们这里介绍两种方案:

  • 基于深度优先查找的一个简单应用:执行一次DFS遍历,并记住顶点变成死端(既退出遍历栈)的顺序,将该次序反过来就得到拓扑排序的一个解,当然,在遍历的时候不能遇到回边。如果遇到一条回边,该图就不是有向无环图,并且对它顶点的拓扑排序是不可能的。

  • 第二种方案是基于减治技术的一个直接实现,不断地做一件事,在余下的所有图中找到源头,它是一个没有输入的节点,然后把它和所有从它出发的边都删除,顶点被删除的次序就是拓扑排序问题的一个解。

  • 这里我们仅仅使用第二种方案进行实现:

Java代码:

package com.result.chart;
import java.util.Stack;
/**
 * @program:算法库
 * @description:拓扑排序实现类
 */

public class TopologicalSorting {
    //各个节点的前驱节点的个数
    int[] NumberOfPrecursorNodes;
    public void result(DenseGraph denseGraph){
        //用于存储节点信息的数组
        boolean[][] g = denseGraph.getG();
        //图中总的节点数
        int node = denseGraph.findNode();
        //实例化
        NumberOfPrecursorNodes = new int[node];
        //初始化我们存储前驱节点个数的数组
        for (int i = 0; i < node; i++) {
            for (int j = 0; j < node; j++) {
                if(g[i][j]){
                    NumberOfPrecursorNodes[j]++;
                }
            }
        }
        //开辟栈空间
        Stack<Integer> stack = new Stack<>();

        //将所有前驱节点个数为0的节点入栈
        for (int i = 1; i < node; i++) {
            if(NumberOfPrecursorNodes[i]==0){
                stack.push(i);
            }
        }
        //开始遍历
        while(!stack.empty()){
            //弹出一个元素
            int temp = stack.pop();
            //输出
            System.out.print(temp+" ");
            //将以该元素为前驱的节点的前驱节点数减一
            for (int i = 0; i < node; i++) {
                if(g[temp][i]){
                    NumberOfPrecursorNodes[i]--;
                    //维护数量数组,一旦等于0了之后立刻入栈
                    if(NumberOfPrecursorNodes[i]==0){
                        stack.push(i);
                    }
                }
            }
        }
    }
}

关于图的DenseGraph 类代码:

/**
 * @program:算法库
 * @description:稠密图的创建,使用邻接矩阵
 */
public class DenseGraph {

    private int edge;           //边数
    private int node;           //节点数
    private boolean direction;  //是否为有向图
    private boolean[][] g;      //图的具体数据

    public boolean[][] getG() {
        return g;
    }

    public DenseGraph(int node, boolean direction){
        assert node>=0;         //一个断言
        this.node = node;
        this.direction = direction;
        this.g = new boolean[node][node];
    }

    //返回边数
    public int findEdge(){
        return edge;
    }
    //返回节点数
    public int findNode(){
        return node;
    }

    //为图添加一条边
    public void addEdge(int p,int q){
        assert p>=0&&p<node;
        assert q>=0&&q<node;

        if(hasEdge(p,q)){
            return;
        }
        g[p][q]=true;
        if(!direction){
            g[q][p]=true;
        }
        edge++;
    }

    //检验p到q是否有一条边
    public boolean hasEdge(int p,int q){
        assert p>=0&&p<node;
        assert q>=0&&q<node;
        return g[p][q];
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值