拓扑排序

一、总体架构

总体架构.png

二、重点说明

1.作用

现实生活中的项目工程、生产活动,都有一个所谓的流程,流程中包含各个步骤,步骤间具有优先级。可以把这个流程抽象为一个有向图。并且有向图中不能有环(因为有环意味着设计逻辑出错)。

2.来源

是对实际问题的抽象。现实生活中的工程项目、生产过程都有一个流程,按照特定的步骤进行执行,步骤之间具有执行的优先级,把这个抽象为有向无环图(DAG图)。

3.定义

4.前提条件

在有向无环图中才可找到拓扑排序,其余的情况找不到拓扑排序。

5. 思想

  1. 选取一个入度为0的点输出
  2. 将该节点、以及由该节点出发的边从图中删除
  3. 重复上述两个步骤,直到当前DAG图为空不存在入度为0的节点。(后一种情况说明图中必有环)

6.实现

此处用图的邻接表进行存储。需要存储每个节点的入度数

public class 拓扑排序 {
    public static void main(String[] args) {
        new 拓扑排序().exe();
    }

    public void exe() {
        // 构建图
        Scanner scan=new Scanner(System.in);
        int N=scan.nextInt();
        // 边数
        int E=scan.nextInt();
        ArcGraph2 g=new ArcGraph2(N,E);
        for(int i=0;i<E;i++){
            int vi=scan.nextInt();
            int vj=scan.nextInt();
            g.insertAdj(vi,vj);
        }
        System.out.println("深度优先遍历********");
        travel(g);
        System.out.println("拓扑排序********");
        topSort(g);
    }
    
    // 遍历图
    public void travel(ArcGraph2 g){
        int[] visited=new int[g.n];
        for(int i=0;i<g.n;i++){
            if(visited[i]==0){
                DFS(g,i,visited);
                System.out.println("***********");
            }
        }
    }
    
    public void DFS(ArcGraph2 g,int v,int[] visited){
        // 从节点v开始访问
        visited[v]=1;
        print(g,v);
        ArcNode p=g.nodes[v].firstArc;
        while(p!=null){
            if(visited[p.num]==0){
                DFS(g,p.num,visited);
            }
            p=p.nextArc;
        }
    }

    private void print(ArcGraph2 g, int v) {
        System.out.println(g.nodes[v].toString());
    }
    
    /**
     * 输出有向无环图的拓扑排序
     * @param g
     * @return:true:拓扑排序成功;flase:不成功
     */
    public boolean topSort(ArcGraph2 g){
        // 计数器,记录已经输出的节点
        int n=0;
        Stack<Integer> stack=new Stack<Integer>();
        // 遍历图的n个节点,将入度为0的节点入栈
        for(int i=0;i<g.n;i++){
            if(g.nodes[i].count==0){
                stack.push(i);
            }
        }
        // 开始访问栈中入度为0的节点
        while(!stack.isEmpty()){
            // 出栈
            int t=stack.pop();
            n++;
            print(g,t);
            // 将该节点,及从该节点出发的边去掉(即对应的入度减1)
            ArcNode p=g.nodes[t].firstArc;
            // 对于每一个邻接节点,入度减1
            while(p!=null){
                g.nodes[p.num].count--;
                if(g.nodes[p.num].count==0){
                    stack.push(p.num);
                }
                p=p.nextArc;
            }
        }
        if(n==g.n){
            return true;
        }else{
            return false;
        }
    }
}

public class ArcGraph2 {

    class ArcNode{
        int num;// 节点编号(从0开始)
        ArcNode nextArc;
        public ArcNode(int num){
            this.num=num;
            this.nextArc=null;
        }
    }
    
    class VNode{
        int num;// 节点编号(从0开始)
        String info;// 节点信息
        int count;// 节点入度
        ArcNode firstArc;
        public VNode(int num){
            this.num=num;
            count=0;
            this.info=null;
            this.firstArc=null;
        }
        @Override
        public String toString() {
            return "VNode [num=" + num + ", info=" + info + ", count=" + count
                    + ", firstArc=" + firstArc + "]";
        }
        
    }
    
    // 节点数
    int n;
    // 边数
    int e;
    
    // 节点数组
    VNode[] nodes;
    
    public ArcGraph2(int n,int e){
        this.n=n;
        this.e=e;
        nodes=new VNode[n];// 因为节点编号从0开始
        for(int i=0;i<n;i++){
            nodes[i]=new VNode(i);
        }
    }

    // 节点vi的邻接节点vj
    public void insertAdj(int vi, int vj) {
        nodes[vj].count++;
        ArcNode p=nodes[vi].firstArc;
        if(p==null){
            nodes[vi].firstArc=new ArcNode(vj);
        }else{
            while(p.nextArc!=null){
                p=p.nextArc;
            }
            p.nextArc=new ArcNode(vj);
        }
        
    }
}

测试

输入的图.png
测试.png

参考文献

数据结构与算法--拓扑排序及无环加权有向图的最短路径

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356