mirror of
https://github.com/schollz/cowyo.git
synced 2023-08-10 21:13:00 +03:00
95 lines
2.2 KiB
Go
95 lines
2.2 KiB
Go
|
package txn
|
||
|
|
||
|
import (
|
||
|
"gopkg.in/mgo.v2/bson"
|
||
|
"sort"
|
||
|
)
|
||
|
|
||
|
func tarjanSort(successors map[bson.ObjectId][]bson.ObjectId) [][]bson.ObjectId {
|
||
|
// http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||
|
data := &tarjanData{
|
||
|
successors: successors,
|
||
|
nodes: make([]tarjanNode, 0, len(successors)),
|
||
|
index: make(map[bson.ObjectId]int, len(successors)),
|
||
|
}
|
||
|
|
||
|
for id := range successors {
|
||
|
id := bson.ObjectId(string(id))
|
||
|
if _, seen := data.index[id]; !seen {
|
||
|
data.strongConnect(id)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sort connected components to stabilize the algorithm.
|
||
|
for _, ids := range data.output {
|
||
|
if len(ids) > 1 {
|
||
|
sort.Sort(idList(ids))
|
||
|
}
|
||
|
}
|
||
|
return data.output
|
||
|
}
|
||
|
|
||
|
type tarjanData struct {
|
||
|
successors map[bson.ObjectId][]bson.ObjectId
|
||
|
output [][]bson.ObjectId
|
||
|
|
||
|
nodes []tarjanNode
|
||
|
stack []bson.ObjectId
|
||
|
index map[bson.ObjectId]int
|
||
|
}
|
||
|
|
||
|
type tarjanNode struct {
|
||
|
lowlink int
|
||
|
stacked bool
|
||
|
}
|
||
|
|
||
|
type idList []bson.ObjectId
|
||
|
|
||
|
func (l idList) Len() int { return len(l) }
|
||
|
func (l idList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||
|
func (l idList) Less(i, j int) bool { return l[i] < l[j] }
|
||
|
|
||
|
func (data *tarjanData) strongConnect(id bson.ObjectId) *tarjanNode {
|
||
|
index := len(data.nodes)
|
||
|
data.index[id] = index
|
||
|
data.stack = append(data.stack, id)
|
||
|
data.nodes = append(data.nodes, tarjanNode{index, true})
|
||
|
node := &data.nodes[index]
|
||
|
|
||
|
for _, succid := range data.successors[id] {
|
||
|
succindex, seen := data.index[succid]
|
||
|
if !seen {
|
||
|
succnode := data.strongConnect(succid)
|
||
|
if succnode.lowlink < node.lowlink {
|
||
|
node.lowlink = succnode.lowlink
|
||
|
}
|
||
|
} else if data.nodes[succindex].stacked {
|
||
|
// Part of the current strongly-connected component.
|
||
|
if succindex < node.lowlink {
|
||
|
node.lowlink = succindex
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if node.lowlink == index {
|
||
|
// Root node; pop stack and output new
|
||
|
// strongly-connected component.
|
||
|
var scc []bson.ObjectId
|
||
|
i := len(data.stack) - 1
|
||
|
for {
|
||
|
stackid := data.stack[i]
|
||
|
stackindex := data.index[stackid]
|
||
|
data.nodes[stackindex].stacked = false
|
||
|
scc = append(scc, stackid)
|
||
|
if stackindex == index {
|
||
|
break
|
||
|
}
|
||
|
i--
|
||
|
}
|
||
|
data.stack = data.stack[:i]
|
||
|
data.output = append(data.output, scc)
|
||
|
}
|
||
|
|
||
|
return node
|
||
|
}
|