使用深度优先搜索对有向无环图进行拓扑排序

来源:互联网 发布:淘宝怎么用erp转订单 编辑:程序博客网 时间:2024/06/05 07:51

拓扑排序是图G中所有节点的一种线性次序,该次序满足以下条件:如果图G包含边(u,v),则节点u在拓扑排序中处于节点v的前面
在实际生活中有很多应用需要用有向无环图来指明事件的优先次序。下面以一个穿衣的例子来讲解拓扑排序:
例子图片
在穿每一件衣服之间,要考虑是否要求先穿上某件衣服。拓扑排序可以解决这个问题,算法的主要思想如下:
通过深度优先搜索,记录每个节点的开始扫描的时间d和将其所有子节点全部扫描完的时间f,按照f从大到小的顺序,从左向右排列,得到的即为拓扑排序。在具体代码操作中,由于先扫描完成的节点总是f最小的,所以将每个扫描完的节点添加到数组中,再按照反序输出即可
具体代码如下:

import java.util.ArrayList;import java.util.LinkedList;import java.util.Scanner;/** *  * @author Founder * 作者原创,转载请注明出处 */public class Main{    static int time = 0;    static ArrayList<Integer> topology;    public static void main(String[] args){        /**         * 输入方式:         * 第一行输入节点的个数n         * 后面n行输入第n个节点(从0开始数)链接的子节点,没有子节点则直接换行         */        Scanner input = new Scanner(System.in);        int n = input.nextInt();        Node[] nodes = new Node[n];        topology = new ArrayList<>();        for(int i = 0; i < n; ++i){            nodes[i] = new Node();        }        input.nextLine();        for(int i = 0; i < n; ++i){            String line = input.nextLine();            LinkedList<Integer> linkNodes = new LinkedList<>();            if(!line.equals("")){                String[] tempIntStr = line.split(" ");                for(int j = 0; j < tempIntStr.length; ++j){                    linkNodes.add(Integer.parseInt(tempIntStr[j]));                }            }            nodes[i].setLinkNodes(linkNodes);        }        dfs(nodes);        for(int i = topology.size() - 1; i >= 0; --i){            System.out.print(topology.get(i) + " ");        }    }    public static void dfs(Node[] nodes){        for(int i = 0; i < nodes.length; ++i){            if(nodes[i].getColor() == Node.WHITE)                dfsVisit(nodes,i);        }    }    public static void dfsVisit(Node[] nodes,int no){        time++;        nodes[no].setColor(Node.GRAY);        nodes[no].setD(time);        LinkedList<Integer> linkNodes = nodes[no].getLinkNodes();        for(int i = 0; i < linkNodes.size(); ++i){            Node temp = nodes[linkNodes.get(i)];            if(temp.getColor() == Node.WHITE){                temp.setParent(nodes[no]);                dfsVisit(nodes,linkNodes.get(i));            }        }        nodes[no].setColor(Node.BLACK);        topology.add(no);        time++;        nodes[no].setF(time);    }}class Node{    public static final int WHITE = 0;    public static final int GRAY = 1;    public static final int BLACK = 2;    private int color = WHITE;    private int d = 0;    private int f = 0;    private Node parent = null;    private LinkedList<Integer> linkNodes = null;    public int getColor() {        return color;    }    public void setColor(int color) {        this.color = color;    }    public int getD() {        return d;    }    public void setD(int d) {        this.d = d;    }    public int getF() {        return f;    }    public void setF(int f) {        this.f = f;    }    public Node getParent() {        return parent;    }    public void setParent(Node parent) {        this.parent = parent;    }    public LinkedList<Integer> getLinkNodes() {        return linkNodes;    }    public void setLinkNodes(LinkedList<Integer> linkNodes) {        this.linkNodes = linkNodes;    }    @Override    public String toString() {        return "Node [d=" + d + ", f=" + f + "]";    }}

对于本例,输入如下:
9
1 7
2 7
5
2 4
5
(没有输入,直接换行)
7
(没有输入,直接换行)
(没有输入,直接换行)

输出:
8 6 3 4 0 1 7 2 5
即按照如下顺序进行:
这里写图片描述
符合题目要求。

基本原理理解:
深度优先搜索森林中,每个树里f最小的节点,没有子节点,因此放在该树的最右边,在回溯时,将其父节点放在左边,使每个树符合拓扑排序的要求;森林中不同树之间不干扰,因此不同树的放置顺序不影响拓扑排序,所以整个排序符合拓扑排序要求。

1 0