拓扑排序的实现原理
实现拓扑排序一般有两种思路,一种基于贪心,一种基于深度优先搜索。接下来分别介绍这两种思路:
1、贪心
1、从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
2、从图中删除该顶点和所有以它为起点的有向边。
3、重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
2、DFS
有向无环图工具类的scala实现,包括拓扑排序(根据入度实现)
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
class DAG[K, V](_nodes: Map[K, V], _edges: Traversable[(K, K)]) {
protected case class Edge(src: K, dst: K)
val nodes = mutable.HashMap[K, V]() ++ _nodes
val edges: Set[Edge] = _edges.map(e => Edge(e._1, e._2)).toSet
private lazy val allNodeKeys = ((nodes.keySet) ++ edges.flatMap(e => Array(e.src, e.dst))).toSet
private lazy val upstreamKeys: Map[K, Set[K]] = edges.groupBy(_.dst).map { case (k, e) => (k, e.map(_.src)) }
private lazy val downstreamKeys: Map[K, Set[K]] = edges.groupBy(_.src).map { case (k, e) => (k, e.map(_.dst)) }
require(!hasCycle, "has cycle")
lazy val topoSortedKeys: Array[K] = {
val inDegree = new mutable.HashMap[K, Int]()
inDegree ++= allNodeKeys.map(k=>(k,0))
inDegree ++= upstreamKeys.map(x=>(x._1, x._2.size))
val queue = mutable.Queue[K]() ++= zeroIndegreeKeys
val res = ArrayBuffer.empty[K]
while(queue.nonEmpty){
val key = queue.dequeue()
res.append(key)
downstreamKeys.get(key).map { downstreams =>
downstreams.map{ dst =>
inDegree(dst) -= 1
inDegree(dst) match {
case 0 => queue.enqueue(dst)
case d if d <0 => throw new IllegalStateException("indegree < 0")
case _ =>
}
}
}
}
res.toArray
}
private def hasCycle: Boolean = {
// 存在环的情况下拓扑排序的结果不能遍历所有节点
allNodeKeys.size != topoSortedKeys.length
}
/**
* 输出拓扑排序结果
*
*/
def topoSorted: Array[(K, Option[V])] = {
topoSortedKeys.map(key => (key, nodes.get(key)))
}
/** *
* 0入度节点
*/
def zeroIndegreeKeys: Array[K] = {
//upstreamKeys只包含有上游的节点,排除0入度节点
(allNodeKeys -- upstreamKeys.keys).toArray
}
/** *
* 获取上游节点
*/
def upstream(key: K): Array[(K, Option[V])] = {
upstreamKeys.getOrElse(key, Set.empty[K]).map(k => (k, nodes.get(k))).toArray
}
/** *
* 获取下游节点
*/
def downstream(key: K): Array[(K, Option[V])] = {
downstreamKeys.getOrElse(key, Set.empty[K]).map(k => (k, nodes.get(k))).toArray
}
/** *
* 更新节点状态
*/
def updateNode(key: K, value: V): Unit = {
nodes.get(key) match {
case None => throw new NoSuchElementException(s"Not Fount $key")
case _ => nodes(key) = value
}
}
}
object DAG {
def apply[K, V](nodes: Map[K, V], edges: Traversable[(K, K)]): DAG[K, V] = new DAG(nodes,edges)
}