拓扑排序 Topological Sort

2018-05-02 16:26:07

一、拓扑排序

有向无环图(Directed acyclic graph,DAG)必定存在拓扑排序;非DAG没有拓扑排序一说。

二、拓扑排序算法

通常拓扑排序算法可以在O(n)的时间复杂度完成,具体来说是O(V + E)

下面以leetcode207为例来介绍拓扑排序算法。

问题描述:

问题求解:

方法一、BFS

使用BFS求解拓扑排序是非常直观和简单的。

维护每个节点的indegree数目,对于入度为0的进队列,将所有入度为0的出队,并更新它们的邻接节点的indegree,若indegree == 0,入队。

循环以上操作直到队列为空。

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<Integer>[] graph = new List[numCourses];
        int[] indegree = new int[numCourses];
        for (int i = 0; i < numCourses; i++) graph[i] = new ArrayList<>();
        for (int[] edge : prerequisites) {
            int from = edge[1];
            int to = edge[0];
            graph[from].add(to);
            indegree[to] += 1;
        }
        int cnt = 0;
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0) q.add(i);
        }
        while (!q.isEmpty()) {
            int curr = q.poll();
            cnt += 1;
            for (int next : graph[curr]) {
                indegree[next] -= 1;
                if (indegree[next] == 0) q.add(next);
            }
        }
        return cnt == numCourses;
    }

  

方法二、DFS

使用DFS代码更为简洁。

实际就是给每个节点打上状态标签,如果访问到了正在访问的节点,那么必定存在环。

另外,我们需要一个访问完成的标签,避免重复访问已经访问过的节点。

    public boolean canFinish(int n, int[][] edges) {
        List<Integer>[] graph = new List[n];
        for (int i = 0; i < n; i++) graph[i] = new ArrayList<>();
        for (int[] e : edges) {
            int from = e[1];
            int to = e[0];
            graph[from].add(to);
        }
        int[] state = new int[n];
        for (int i = 0; i < n; i++) {
            if (state[i] == 0 && !dfs(graph, i, state)) return false; 
        }
        return true;
    }
    
    private boolean dfs(List<Integer>[] graph, int node, int[] state) {
        state[node] = 1;
        for (int next : graph[node]) {
            if (state[next] == 2) continue; 
            if (state[next] == 1) return false;
            if (!dfs(graph, next, state)) return false;
        }
        state[node] = 2;
        return true;
    }

三、拓扑排序应用

  • 210. Course Schedule II

问题描述:

问题求解:

也是一条裸的拓扑排序题,相较于上一题,本题可以说是更纯粹的拓扑排序,因为不仅需要判环,还需要输出一个合法的解。当然,算法实现上也是两种思路,一是DFS,而是kahn算法。

DFS:

    public int[] findOrder(int numCourses, int[][] prerequisites) {
        int[] res = new int[numCourses];
        List<Integer>[] graph = new List[numCourses];
        for (int i = 0; i < numCourses; i++) graph[i] = new ArrayList<>();
        for (int[] pair : prerequisites) {
            graph[pair[1]].add(pair[0]);
        }
        int[] state = new int[numCourses];
        Stack<Integer> s = new Stack<>();
        for (int i = 0; i < numCourses; i++) {
            if (state[i] == 0)
                if (!dfs(graph, state, s, i)) return new int[0];
        }
        for (int i = 0; i < numCourses; i++) res[i] = s.pop();
        return res;
    }

    private boolean dfs(List<Integer>[] g, int[] state, Stack<Integer> s, int i) {
        if (state[i] == 2) return true;
        if (state[i] == 1) return false;
        state[i] = 1;
        for (int node : g[i]) {
            if (!dfs(g, state, s, node)) return false;
        }
        state[i] = 2;
        s.push(i);
        return true;
    }

Kahn:

    public int[] findOrder(int numCourses, int[][] prerequisites) {
        int[] res = new int[numCourses];
        int[] indegree = new int[numCourses];
        List<Integer>[] graph = new List[numCourses];
        for (int i = 0; i < numCourses; i++) graph[i] = new ArrayList<>();
        for (int[] pair : prerequisites) {
            graph[pair[1]].add(pair[0]);
            indegree[pair[0]]++;
        }
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0)
                q.add(i);
        }
        int cnt = 0;
        while (!q.isEmpty()) {
            int p = q.poll();
            res[cnt++] = p;
            for (int node : graph[p]) {
                if (--indegree[node] == 0) q.add(node);
            }
        }
        if (cnt == numCourses) return res;
        else return new int[0];
    }
  • 1203. Sort Items by Groups Respecting Dependencies

问题描述

问题求解

    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        Map<Integer, Set<Integer>> group2items = new HashMap<>();
        Map<Integer, Set<Integer>> group_g = new HashMap<>();
        Map<Integer, Integer> g_indegree = new HashMap<>();
        Map<Integer, Set<Integer>> item_g = new HashMap<>();
        Map<Integer, Integer> i_indegree = new HashMap<>();
        
        int num_of_groups = m;
        
        for (int i = 0; i < group.length; i++) {
            if (group[i] == -1) group[i] = num_of_groups++;
        }
        
        for (int i = 0; i < num_of_groups; i++) {
            group2items.put(i, new HashSet<>());
            group_g.put(i, new HashSet<>());
            g_indegree.put(i, 0);
        }
        
        for (int i = 0; i < n; i++) {
            item_g.put(i, new HashSet<>());
            i_indegree.put(i, 0);
            group2items.get(group[i]).add(i);
        }
        
        for (int to = 0; to < beforeItems.size(); to++) {
            int to_group = group[to];
            for (int from : beforeItems.get(to)) {
                int from_group = group[from];
                if (to_group == from_group) {
                    item_g.get(from).add(to);
                    i_indegree.put(to, i_indegree.get(to) + 1);
                }
                else {
                    if (!group_g.get(from_group).contains(to_group)) {
                        group_g.get(from_group).add(to_group);
                        g_indegree.put(to_group, g_indegree.get(to_group) + 1);
                    }
                }
            }
        }
        
        // check groups
        List<Integer> groups = new ArrayList<>();
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < num_of_groups; i++) {
            if (g_indegree.get(i) == 0) {
                q.add(i);
                groups.add(i);
            }
        }
        while (!q.isEmpty()) {
            int cur = q.poll();
            for (int to : group_g.get(cur)) {
                g_indegree.put(to, g_indegree.get(to) - 1);
                if (g_indegree.get(to) == 0) {
                    q.add(to);
                    groups.add(to);
                }
            }
        }
        if (groups.size() != num_of_groups) return new int[0];
        
        // check items 
        List<Integer> res = new ArrayList<>();
        for (int g : groups) {
            int num = 0;
            q = new LinkedList<>();
            for (int item : group2items.get(g)) {
                if (i_indegree.get(item) == 0) {
                    q.add(item);
                    res.add(item);
                    num += 1;
                }
            }
            
            while (!q.isEmpty()) {
                int cur = q.poll();
                for (int to : item_g.get(cur)) {
                    i_indegree.put(to, i_indegree.get(to) - 1);
                    if (i_indegree.get(to) == 0) {
                        q.add(to);
                        res.add(to);
                        num += 1;
                    }
                }
            }
            if (num != group2items.get(g).size()) return new int[0];
        }
        int[] ret = new int[res.size()];
        for (int i = 0; i < res.size(); i++) ret[i] = res.get(i);
        return ret;
    }

  

原文地址:https://www.cnblogs.com/hyserendipity/p/8980954.html