14.1 Kahn算法
文章目录
- 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算法相关推荐
- [吴恩达机器学习笔记]14降维3-4PCA算法原理
14.降维 觉得有用的话,欢迎一起讨论相互学习~Follow Me 14.3主成分分析原理Proncipal Component Analysis Problem Formulation 主成分分析( ...
- 拓扑排序----Kahn算法和字典序最小的拓扑排序
一.拓扑排序定义: 二.卡恩算法(Kahn): 1.Kahn算法介绍: 有向无环图DAG至少具有一个度数为0的顶点和一个度数为0的顶点. 证明:上述事实有一个简单的证明,即DAG不包含循环,这意味着所 ...
- 算法 64式 14、排序算法整理_1_1到15题
1 算法思想 这里将寻找最小/大的前k个数,寻找逆序对,线性时间选择(寻找第k小/大的元素),奇偶/大小写字符分别放在前后部分等和排序相关类型的题目,放在了排序而不是查找中. 1.1含义 排序含义:重 ...
- Kahn算法-拓扑排序
/* 时间:2015/11/22内容:拓扑排序的实现(可用于判断图中是否有环路)概述:基于Kahn算法(DFS算法的实现),采用邻接表的方式来存储有向无权图,并实现对图的拓扑排序,在给出拓扑序列的同时 ...
- WEEK(8)作业——差分约束、拓扑排序(Kahn算法)、SCC(强连通分量)、DFS序、Kosaraju算法
A-区间选点Ⅱ 题目描述 给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点 使用差分约束系统的解法解决这道题 Input 输入第一行一个 ...
- 第3章 感受(一)——3.14. Hello STL 算法篇
[回到目录] 白话C++ 3.14. Hello STL 算法篇 前一小节,在输出成绩时,我们从students中得到每个学生的学号,然后,通过for循环,在一个list中查找符合"学号等于 ...
- 图论算法—图的拓扑排序介绍和Kahn算法原理解析以及Java代码的实现
详细介绍了图的拓扑排序的概念,然后介绍了求拓扑序列的算法:Kahn算法的原理,最后提供了基于邻接矩阵和邻接表的图对该算法的Java实现. 阅读本文需要一定的图的基础,如果对于图不是太明白的可以看看这篇 ...
- 拓扑排序总结(Kahn算法)
随便记录点东西 在拓扑排序中,排序结果唯一当且仅当所有顶点间具有全序关系. 快速排序不是稳定的(偏序关系),因为相同元素之间的关系无法确定. 插入排序是稳定的(全序关系),因为相同元素可以通过位置的先 ...
- (数据结构)有向图的拓扑排序 Kahn算法
拓扑排序是对有向无圈图的顶点的一种排序,使得如果存在一条从vi到vj的路径,那么在排序中,vi必须出现在vj的前面. 首先,我们需要准备一个vector<int> link[maxn],l ...
最新文章
- Docker基础之九: 管理容器的数据
- 小米手环4怎么使用_小米手环4/5 NFC添加加密门禁
- vb表格控件_(超级干货)ExcelVBA拆分表格并分别发送邮件增强版
- python常用_Python常用小技巧
- Python3 字符串拼接
- 洛谷P1659 养猪
- url的关键字不知道是uft-8还是GBK
- cpu工作原理flash动画_17张PLC工作原理动画,每一个都是经典
- SUSE12SP3-Mycat(4)rule.xml配置详解
- Linux命令(20)linux服务器之间复制文件和目录
- 2.1.3 Sorting a Three-Valued Sequence 三值的排序
- 离散数学 | ∅ 与 {∅} 出现在离散数学幂集合中
- 如何在mysql navicat 设置datetime类型的默认事件值
- ffmpeg海康SDK流接入的支持
- BT种子下载软件uTorrent Pro v3.5.5.45972
- Unity TalkingData接入
- 免费的安装算量软件有哪些?鹏业安装算量与品茗安装算量对比
- 怎样在线压缩png图片大小?将这个压缩方法分享给大家
- 推荐算法(一)——FM因式分解机
- 安卓如何将数据转到iPhone上?
热门文章
- maximum number (256) of shader keywords exceeded unity的报错解决方法
- React学习笔记_shoping_cart
- 第四阶段vue项目总结(新手小白一枚,不建议参考。 )
- PHP搭建简单的留言板论坛
- 宝马将系统架上微软Azure,国内科技巨头也难抵汽车“诱惑”
- 2021年中国民爆行业企业经营情况及主要产品产销量分析:工业雷管、工业炸药产销量均有所下滑[图]
- 普通人的2022前端秋招总结
- WD My Cloud Gen2 安装第三方应用
- App Store付费应用退款流程(2015超级详细版)
- 2023全新个人免签约支付系统PHP源码 码支付系统 ThinkPHP6框架全开源 starpay2.0Beta