文章目录

  • 1 拓扑排序的实用性
  • 2 入度表算法(也叫BFS算法或Kahn算法)
  • 3 Java实现
  • 4 Python实现

1 拓扑排序的实用性

  生活中,事物之间总存在着“依赖”关系。而怎么计算依赖关系,这就是拓扑排序的研究内容。我先举个例子(图片来源于网络)
  这和先有鸡还是先有蛋是同样的性质。我们用图算法来表示就是:

  这一类问题是无法拓扑排序的。只要有环的存在,就无法计算依赖关系。我们再来看另一个过程:

  这个过程顺序就明确了,先赚钱,再买房、买车、准备彩礼,最后结婚。上述的婚姻流程图由于不存在环,所以可以计算执行顺序,也就是说,可以计算拓扑排序。此外,在计算依赖的应用中,比如maven、工作流、决策引擎等应用中,拓扑排序都十分重要。

2 入度表算法(也叫BFS算法或Kahn算法)

  Kahn算法又叫做入度表算法。这个算法是要找出入度为0的点。入度为0,就是没有有任何节点指向它。也就是只做from,不做to的节点。用大白话说,就是第一步要执行的节点。当处理完了入度为0的点,之后在将这些点删除,于是就有了新的入度为0的点,然后再执行。直到所有点处理完毕,拓扑排序结束。以上述的婚姻流程图为例子:
  Step1, 处理入度为0的点:

  Step2, 删除为0的点:

  Step3, 再处理入度为0的点:

  Step4, 再删除入度为0的点:

  后续的步骤我就不画图了。所以拓扑排序是:赚钱、买房、买车、彩礼、结婚。可以看出来这是一种BFS搜索算法,所以也叫BFS拓扑排序

3 Java实现

  可以使用一个hashmap来表示入度表。因为hashmap key可以为null。
  所以可以这样写入度表:

public class DagJoinTable extends HashMap<Integer, List<Integer>> {public void add(Integer key, Integer value) {List<Integer> integers = this.computeIfAbsent(key, k -> new ArrayList<>());// 这里需要避免重复添加if (!integers.contains(value)) {integers.add(value);}}public void remove(Integer key, Integer value) {final List<Integer> integers = get(key);if (integers != null && !integers.isEmpty()) {while (integers.remove(value)) {// 循环删除}}}
}

  迭代器的代码:

public class DagIterator<E> implements Iterator<E> {/*** key 为 from value 为 to*/private DagJoinTable joinTable;private DirectedAcyclicGraph<E> graph;/*** 假设无环图* 逆连接表为* A -> B* B -> C* C -> D* 虚构 NULL -> A** @param graph*/public DagIterator(DirectedAcyclicGraph<E> graph) {this.joinTable = new DagJoinTable();// build逆连接表, 逆连接表里null key的创建特别难啊//  A -> B 这个边,A 放入null KEY B 移出null keyfinal List<? extends Edge>[] edges = graph.edges;for (List<? extends Edge> edgePerVertex : edges) {// 每个点的边for (Edge edge : edgePerVertex) {final int from = edge.getFrom();joinTable.add(null, from);}}for (List<? extends Edge> edgePerVertex : edges) {for (Edge edge : edgePerVertex) {final int from = edge.getFrom();final int to = edge.getTo();joinTable.add(from, to);joinTable.remove(null, to);}}this.graph = graph;}@Overridepublic boolean hasNext() {final List<Integer> integers = joinTable.get(null);return integers != null && !integers.isEmpty();}/*** NULL -> A* A -> B,C* B -> C* C -> D* 如果移出A 后* A 所指向的B 放入NULL KEY* 变成* NULL -> B, C* B -> C* C -> D* 这里对于C,因为C存在B->C,所以不能有null -> C* 所以算法是** @return*/@Overridepublic E next() {final List<Integer> integers = joinTable.get(null);if (integers == null || integers.isEmpty()) {throw new NoSuchElementException();}final Integer remove = integers.remove(0);// 移除之后final List<Integer> candidates = joinTable.get(remove);if (candidates != null) {for (Integer candidate : candidates) {joinTable.add(null, candidate);}for (Integer candidate : candidates) {final List<Integer> candidateValues = joinTable.get(candidate);if (candidateValues != null) {for (Integer candidateValue : candidateValues) {joinTable.remove(null, candidateValue);}}}}if (integers.isEmpty()) {joinTable.remove(null);}return graph.vertices[remove];}
}

4 Python实现

  Python的实现,我没有使用hashmap,而是直接用一个布尔数组连存储入度为0的元素,然后使用双色BFS进行拓扑排序。为了简单,我图的实现是用的邻接矩阵。

# _*_ coding:utf-8 _*_class UnweightedGraph:def __init__(self):self.__vertices = []self.__edges = []@propertydef vertices(self):return self.__vertices@vertices.setterdef vertices(self, value):self.__vertices = value@propertydef edges(self):return self.__edges@edges.setterdef edges(self, value):self.__edges = valuewhite = 0gray = 1black = 2def topological_sort(self):result = []# 0入度元素s = [True for _ in self.__vertices]for edges in self.__edges:for e in edges:if e and i in vertices:circle_exists = False# 简单BFSvisited = [False for _ in self.__vertices]queue = [i for i, e in enumerate(s) if e]while len(queue) > 0:e = queue.pop()# 元素if not visited[e]:for edge in self.__edges[e]:if not visited[edge]:queue.append(edge)result.append(self.__vertices[e])visited[e] = Truereturn result

  测试数据:

# _*_ coding:utf-8 _*_class UnweightedGraph:def __init__(self):self.__vertices = []self.__edges = []@propertydef vertices(self):return self.__vertices@vertices.setterdef vertices(self, value):self.__vertices = value@propertydef edges(self):return self.__edges@edges.setterdef edges(self, value):self.__edges = valuewhite = 0gray = 1black = 2def topological_sort(self):result = []# copy一份点集合,存索引vertices = [i for i, _ in enumerate(self.__vertices)]# 当集合内还有元素while len(vertices) > 0:# 0入度元素zero_in_degrees = [True for _ in self.__vertices]for vertex in vertices:for e in self.__edges[vertex]:if zero_in_degrees[e]:zero_in_degrees[e] = False# 如果没有0入度表元素则表示存在环circle_exists = Truefor e in zero_in_degrees:if e:circle_exists = Falseif circle_exists:raise RuntimeError('存在环')# 按顺序加入resultfor i, e in enumerate(zero_in_degrees):if e and i in vertices:# 删除元素vertices.remove(i)result.append(self.__vertices[i])return result

  测试结果:

['赚钱', '彩礼', '结婚', '买车', '买房']

14.1 Kahn算法相关推荐

  1. [吴恩达机器学习笔记]14降维3-4PCA算法原理

    14.降维 觉得有用的话,欢迎一起讨论相互学习~Follow Me 14.3主成分分析原理Proncipal Component Analysis Problem Formulation 主成分分析( ...

  2. 拓扑排序----Kahn算法和字典序最小的拓扑排序

    一.拓扑排序定义: 二.卡恩算法(Kahn): 1.Kahn算法介绍: 有向无环图DAG至少具有一个度数为0的顶点和一个度数为0的顶点. 证明:上述事实有一个简单的证明,即DAG不包含循环,这意味着所 ...

  3. 算法 64式 14、排序算法整理_1_1到15题

    1 算法思想 这里将寻找最小/大的前k个数,寻找逆序对,线性时间选择(寻找第k小/大的元素),奇偶/大小写字符分别放在前后部分等和排序相关类型的题目,放在了排序而不是查找中. 1.1含义 排序含义:重 ...

  4. Kahn算法-拓扑排序

    /* 时间:2015/11/22内容:拓扑排序的实现(可用于判断图中是否有环路)概述:基于Kahn算法(DFS算法的实现),采用邻接表的方式来存储有向无权图,并实现对图的拓扑排序,在给出拓扑序列的同时 ...

  5. WEEK(8)作业——差分约束、拓扑排序(Kahn算法)、SCC(强连通分量)、DFS序、Kosaraju算法

    A-区间选点Ⅱ 题目描述 给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点 使用差分约束系统的解法解决这道题 Input 输入第一行一个 ...

  6. 第3章 感受(一)——3.14. Hello STL 算法篇

    [回到目录] 白话C++ 3.14. Hello STL 算法篇 前一小节,在输出成绩时,我们从students中得到每个学生的学号,然后,通过for循环,在一个list中查找符合"学号等于 ...

  7. 图论算法—图的拓扑排序介绍和Kahn算法原理解析以及Java代码的实现

    详细介绍了图的拓扑排序的概念,然后介绍了求拓扑序列的算法:Kahn算法的原理,最后提供了基于邻接矩阵和邻接表的图对该算法的Java实现. 阅读本文需要一定的图的基础,如果对于图不是太明白的可以看看这篇 ...

  8. 拓扑排序总结(Kahn算法)

    随便记录点东西 在拓扑排序中,排序结果唯一当且仅当所有顶点间具有全序关系. 快速排序不是稳定的(偏序关系),因为相同元素之间的关系无法确定. 插入排序是稳定的(全序关系),因为相同元素可以通过位置的先 ...

  9. (数据结构)有向图的拓扑排序 Kahn算法

    拓扑排序是对有向无圈图的顶点的一种排序,使得如果存在一条从vi到vj的路径,那么在排序中,vi必须出现在vj的前面. 首先,我们需要准备一个vector<int> link[maxn],l ...

最新文章

  1. Docker基础之九: 管理容器的数据
  2. 小米手环4怎么使用_小米手环4/5 NFC添加加密门禁
  3. vb表格控件_(超级干货)ExcelVBA拆分表格并分别发送邮件增强版
  4. python常用_Python常用小技巧
  5. Python3 字符串拼接
  6. 洛谷P1659 养猪
  7. url的关键字不知道是uft-8还是GBK
  8. cpu工作原理flash动画_17张PLC工作原理动画,每一个都是经典
  9. SUSE12SP3-Mycat(4)rule.xml配置详解
  10. Linux命令(20)linux服务器之间复制文件和目录
  11. 2.1.3 Sorting a Three-Valued Sequence 三值的排序
  12. 离散数学 | ∅ 与 {∅} 出现在离散数学幂集合中
  13. 如何在mysql navicat 设置datetime类型的默认事件值
  14. ffmpeg海康SDK流接入的支持
  15. BT种子下载软件uTorrent Pro v3.5.5.45972
  16. Unity TalkingData接入
  17. 免费的安装算量软件有哪些?鹏业安装算量与品茗安装算量对比
  18. 怎样在线压缩png图片大小?将这个压缩方法分享给大家
  19. 推荐算法(一)——FM因式分解机
  20. 安卓如何将数据转到iPhone上?

热门文章

  1. maximum number (256) of shader keywords exceeded unity的报错解决方法
  2. React学习笔记_shoping_cart
  3. 第四阶段vue项目总结(新手小白一枚,不建议参考。 )
  4. PHP搭建简单的留言板论坛
  5. 宝马将系统架上微软Azure,国内科技巨头也难抵汽车“诱惑”
  6. 2021年中国民爆行业企业经营情况及主要产品产销量分析:工业雷管、工业炸药产销量均有所下滑[图]
  7. 普通人的2022前端秋招总结
  8. WD My Cloud Gen2 安装第三方应用
  9. App Store付费应用退款流程(2015超级详细版)
  10. 2023全新个人免签约支付系统PHP源码 码支付系统 ThinkPHP6框架全开源 starpay2.0Beta