ここは私自身の解決策です。さらに、入力で検出された可能なループを返します。
呼び出し元が訪問者に がノードとコールバックを取得し、参照ノードごとにコールバックを呼び出すため、ノードの形式は固定されていません。
ループのレポート作成が不要な場合は、簡単に削除できます。
import TopologicalSort._
def visitor(node: BaseNode, callback: (Node) => Unit): Unit = {
node.referenced.foreach(callback)
}
assertEquals("Previous order is kept",
Vector(f, a, b, c, d, e),
topoSort(Vector(f, a, b, c, d, e), visitor).result)
assertEquals(Vector(a, b, c, d, f, e),
topoSort(Vector(d, c, b, f, a, e), visitor).result)
複雑さにいくつかの考え:これは次のように適用される質問の例では
import scala.collection.mutable
// Based on https://en.wikipedia.org/wiki/Topological_sorting?oldformat=true#Depth-first_search
object TopologicalSort {
case class Result[T](result: IndexedSeq[T], loops: IndexedSeq[IndexedSeq[T]])
type Visit[T] = (T) => Unit
// A visitor is a function that takes a node and a callback.
// The visitor calls the callback for each node referenced by the given node.
type Visitor[T] = (T, Visit[T]) => Unit
def topoSort[T <: AnyRef](input: Iterable[T], visitor: Visitor[T]): Result[T] = {
// Buffer, because it is operated in a stack like fashion
val temporarilyMarked = mutable.Buffer[T]()
val permanentlyMarked = mutable.HashSet[T]()
val loopsBuilder = IndexedSeq.newBuilder[IndexedSeq[T]]
val resultBuilder = IndexedSeq.newBuilder[T]
def visit(node: T): Unit = {
if (temporarilyMarked.contains(node)) {
val loopStartIndex = temporarilyMarked.indexOf(node)
val loop = temporarilyMarked.slice(loopStartIndex, temporarilyMarked.size)
.toIndexedSeq
loopsBuilder += loop
} else if (!permanentlyMarked.contains(node)) {
temporarilyMarked += node
visitor(node, visit)
permanentlyMarked += node
temporarilyMarked.remove(temporarilyMarked.size - 1, 1)
resultBuilder += node
}
}
for (i <- input) {
if (!permanentlyMarked.contains(i)) {
visit(i)
}
}
Result(resultBuilder.result(), loopsBuilder.result())
}
}
このソリューションの最悪の場合の複雑さは、実際にOを超えています( n + m)。ノードごとにtemporarilyMarked
アレイがスキャンされるためです。
temporarilyMarked
をたとえばHashSet
に置き換えると、漸近的な複雑さが改善されます。
ノード内にマークを直接格納すると真のO(n + m)が達成されますが、それらを外部に格納すると汎用ソリューションの作成が容易になります。
私はパフォーマンステストを実行していませんが、それは非常に深くない限り、大きなグラフであってもtemporarilyMarked
アレイのスキャンは問題ではないと思われます。
データにサイクルが存在するときに複数回実行すると安定したこのコードのバージョンを後で作成しましたが、これは投票していないためここに追加することはありません。ループの処理を改善する必要がある場合は、コメントを追加してください。 –