图的拓扑序

856 阅读2分钟

「这是我参与2022首次更文挑战的第40天,活动详情查看:2022首次更文挑战」。

一、什么是图的拓扑排序?

图的拓扑排序:根据先后顺序,能够一次把工作顺利做完,而且不缺依赖的一个顺序,所以就是所谓的拓扑序,不能出现循环依赖或环

对于拓扑排序一定是有向图,一定无环,即有向无环图,因为有环的话,就不知道是什么样的顺序了

拓扑排序不唯一

二、题目一

image.png

图的拓扑排序结果为2种: (A,D,E,F),(A,E,D,F)

1、分析

利用自定义图的in(入度)

A出去的时候,D和E的in减1

利用自定义图结构的入度(in),in为0代表指零个指向它的节点,按照这个原则实现图的拓扑排序

2、实现

// directed graph and no loop
public static List<Node> sortedTopology(Graph graph) {
    // key 某个节点   value 剩余的入度
    Map<Node, Integer> inMap = new HashMap<>();
    // 只有剩余入度为0的点,才进入这个队列
    Queue<Node> zeroInQueue = new LinkedList<>();
    for (Node node : graph.nodes.values()) {
        inMap.put(node, node.in);
        if (node.in == 0) {
            zeroInQueue.add(node);
        }
    }
    List<Node> result = new ArrayList<>();
    while (!zeroInQueue.isEmpty()) {
        Node cur = zeroInQueue.poll();
        result.add(cur);
        for (Node next : cur.nexts) {
            inMap.put(next, inMap.get(next) - 1);
            if (inMap.get(next) == 0) {
                zeroInQueue.add(next);
            }
        }
    }
    return result;
}

三、题目二

image.png

linkcode

1、分析

图的拓扑排序一定是有向无环图,可以利用点次大小从大到小排序

x->y->z

如果y点次90的话,x的点次一定大于90,所以如果x的点次>=y的点次,那么x一定在y的前边,也就是拓扑序这么排没问题

假如x点从自己往后走一遍,点次算一遍,得出x点次,y往后走一遍,算一遍点次,或者 a从自己出发也的算一遍,这算的过程,之前有算过,意思就是走过的路,能不能记录下来,等到用到的时候,直接拿结果,岂不是快哉,可以用记忆化缓存来记录,提升效率

记忆化搜索(记忆化缓存 或 杀缓存)

2、实现

2.1、方法一

public static ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
    HashMap<DirectedGraphNode, Integer> indegreeMap = new HashMap<>();
    for (DirectedGraphNode cur : graph) {
        indegreeMap.put(cur, 0);
    }
    for (DirectedGraphNode cur : graph) {
        for (DirectedGraphNode next : cur.neighbors) {
            indegreeMap.put(next, indegreeMap.get(next) + 1);
        }
    }
    Queue<DirectedGraphNode> zeroQueue = new LinkedList<>();
    for (DirectedGraphNode cur : indegreeMap.keySet()) {
        if (indegreeMap.get(cur) == 0) {
            zeroQueue.add(cur);
        }
    }
    ArrayList<DirectedGraphNode> ans = new ArrayList<>();
    while (!zeroQueue.isEmpty()) {
        DirectedGraphNode cur = zeroQueue.poll();
        ans.add(cur);
        for (DirectedGraphNode next : cur.neighbors) {
            indegreeMap.put(next, indegreeMap.get(next) - 1);
            if (indegreeMap.get(next) == 0) {
                zeroQueue.offer(next);
            }
        }
    }
    return ans;
}

2.2、方法二

public static class Record {
    public DirectedGraphNode node;
    public long nodes;

    public Record(DirectedGraphNode n, long o) {
        node = n;
        nodes = o;
    }
}

public static ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
    HashMap<DirectedGraphNode, Record> order = new HashMap<>();
    for (DirectedGraphNode cur : graph) {
        f(cur, order);
    }
    ArrayList<Record> recordArr = new ArrayList<>();
    for (Record r : order.values()) {
        recordArr.add(r);
    }
    recordArr.sort((o1, o2) -> Long.compare(o2.nodes, o1.nodes));
    ArrayList<DirectedGraphNode> ans = new ArrayList<>();
    for (Record r : recordArr) {
        ans.add(r.node);
    }
    return ans;
}

// 当前来到cur点,请返回cur点所到之处,所有的点次!
// 返回(cur,点次)
// 缓存!!!!!order
//  key : 某一个点的点次,之前算过了!
//  value : 点次是多少
public static Record f(DirectedGraphNode cur, HashMap<DirectedGraphNode, Record> order) {
    if (order.containsKey(cur)) {
        return order.get(cur);
    }
    // cur的点次之前没算过!
    long nodes = 0;
    for (DirectedGraphNode next : cur.neighbors) {
        nodes += f(next, order).nodes;
    }
    Record ans = new Record(cur, nodes + 1);
    order.put(cur, ans);
    return ans;
}

四、总结

利用自定义图结构的入度(in),in为0代表指零个指向它的节点,按照这个原则实现图的拓扑排序

图的拓扑排序算法:

  1. 在图中找到所有入度为0的点输出
  2. 把所有入度为0的点在图中删掉,继续找入度为0的点输出,周而复始
  3. 图的所有点都被删除后,依次输出的顺序就是拓扑排序

要求:有向图且其中没有环

应用:事件安排、编译顺序