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;
}

  

拓扑排序 Topological Sort的更多相关文章

  1. LeetCode编程训练 - 拓扑排序(Topological Sort)

    拓扑排序基础 拓扑排序用于解决有向无环图(DAG,Directed Acyclic Graph)按依赖关系排线性序列问题,直白地说解决这样的问题:有一组数据,其中一些数据依赖其他,问能否按依赖关系排序 ...

  2. 算法与数据结构基础 - 拓扑排序(Topological Sort)

    拓扑排序基础 拓扑排序用于解决有向无环图(DAG,Directed Acyclic Graph)按依赖关系排线性序列问题,直白地说解决这样的问题:有一组数据,其中一些数据依赖其他,问能否按依赖关系排序 ...

  3. 【数据结构与算法Python版学习笔记】图——拓扑排序 Topological Sort

    概念 很多问题都可转化为图, 利用图算法解决 例如早餐吃薄煎饼的过程 制作松饼的难点在于知道先做哪一步.从图7-18可知,可以首先加热平底锅或者混合原材料.我们借助拓扑排序这种图算法来确定制作松饼的步 ...

  4. 拓扑排序 (Topological Sorting)

    拓扑排序(Topological Sorting) 一.拓扑排序 含义 构造AOV网络全部顶点的拓扑有序序列的运算称为拓扑排序(Topological Sorting). 在图论中,拓扑排序(Topo ...

  5. [MIT6.006] 14. Depth-First Search (DFS), Topological Sort 深度优先搜索,拓扑排序

    一.深度优先搜索 它的定义是:递归探索图,必要时要回溯,同时避免重复. 关于深度优先搜索的伪代码如下: 左边DFS-Visit(V, Adj.s)是只实现visit所有连接某个特定点(例如s)的其他点 ...

  6. 拓扑排序(Toposort)

    摘自:https://blog.csdn.net/qq_35644234/article/details/60578189 <图论算法> 1.拓扑排序的介绍 对一个有向无环图(Direct ...

  7. [LeetCode] 207. 课程表(拓扑排序,BFS)

    题目 现在你总共有 n 门课需要选,记为 0 到 n-1. 在选修某些课程之前需要一些先修课程. 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1] 给定课程总量 ...

  8. BFS (1)算法模板 看是否需要分层 (2)拓扑排序——检测编译时的循环依赖 制定有依赖关系的任务的执行顺序 djkstra无非是将bfs模板中的deque修改为heapq

    BFS模板,记住这5个: (1)针对树的BFS 1.1 无需分层遍历 from collections import deque def levelOrderTree(root): if not ro ...

  9. 拓扑排序(三)之 Java详解

    前面分别介绍了拓扑排序的C和C++实现,本文通过Java实现拓扑排序. 目录 1. 拓扑排序介绍 2. 拓扑排序的算法图解 3. 拓扑排序的代码说明 4. 拓扑排序的完整源码和测试程序 转载请注明出处 ...

随机推荐

  1. cordova+Android Studio 1.0+ionic+win7(转)

    转自http://blog.csdn.net/fuyunww/article/details/42216125 目录(?)[-] 在项目目录下执行 a创建工程 b添加平台支持 c添加插件在Androi ...

  2. Java互联网架构-Mysql分库分表订单生成系统实战分析

    概述 分库分表的必要性 首先我们来了解一下为什么要做分库分表.在我们的业务(web应用)中,关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量.连接数.处理能力等都很有限,数据库本身的“有状态性” ...

  3. 【转】编程思想之多线程与多进程(3)——Java中的多线程

    <编程思想之多线程与多进程(1)——以操作系统的角度述说线程与进程>一文详细讲述了线程.进程的关系及在操作系统中的表现,这是多线程学习必须了解的基础.本文将接着讲一下Java中多线程程序的 ...

  4. PowerDesigner逆向操作(从mysql5.0生成数据库的物理模型),把Comment写到name中,pdm文件导出为word

    PowerDesigner逆向操作(从mysql5.0生成数据库的物理模型) 环境:powderdesigner12.5:mysql5.0步骤:1. 为指定的数据库配置mysql的ODBC数据源先下载 ...

  5. mysql下的将多个字段名的值复制到另一个字段名中(批量更新数据)字符串拼接cancat实战例子

    mysql下的将多个字段名的值复制到另一个字段名中(批量更新数据)mysql字符串拼接cancat实战例子: mysql update set 多个字段相加,如果是数字相加可以直接用+号(注:hund ...

  6. HTML5 manifest离线缓存技术

    干什么用的? 离线缓存为的是第一次请求后,根据manifest文件进行本地缓存,并且在下一次请求后进行展示(若有缓存的话,无需再次进行请求而是直接调用缓存),最根本的感觉是它使得WEB从online可 ...

  7. MySQL之表连接(内外连接和重命名的使用)

    #要多练练 1.连接查询根据连接方式分为 内连接 等值连接 非等值连接 自连接 外连接 左外连接(左连接) 右外连接(右连接) 当多张表进行连接查询,若没有任何条件进行限制,会 发生什么现象? 会出现 ...

  8. Jsoup请求http或https返回json字符串工具类

    Jsoup请求http或https返回json字符串工具类 所需要的jar包如下: jsoup-1.8.1.jar 依赖jar包如下: httpclient-4.5.4.jar; httpclient ...

  9. VIM 使用心得

    序 到百度外卖任职以后,发现在我们部门无论 mac 还是 windows,程序员们清一色地都在使用 VIM 来编辑代码,期间穿插着各种插件.快捷键.眼花缭乱的命令.我在大学时只会极少的 VIM 命令, ...

  10. C++设计模式 之 “接口隔离” 模式:Facade、Proxy、Mediator、Adapter

    “接口隔离”模式 在组建构建过程中,某些接口之间之间的依赖常常会带来很多问题.甚至根本无法实现.采用添加一层间接(稳定)接口,来隔离本来相互紧密关联的接口是一种常见的解决方案. 典型模式 #Facad ...