zoukankan      html  css  js  c++  java
  • 拓扑排序 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;
        }
    

      

  • 相关阅读:
    flutter android 开发笔记(三.flutter和android数据交互)
    flutter android 开发笔记(二.module集成,混合页面)
    flutter android 开发笔记(一.集成)
    svn主干开辟分支、分支合并到主干
    svn主干开辟分支、分支合并到主干
    打包aar时把依赖的jar打包进aar中
    as修改已有项目的svn地址
    Flutter踩坑日记(一)
    android中两个不同名称的app不能同时安装
    aar、jar、so的引入和aar打包包含so、aar、jar文件
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/8980954.html
Copyright © 2011-2022 走看看