凤凰求蛊
你的“Less”函数不是可传递的。也就是说,如果 A < B 且 B < C,那么它也必须成立 A < C。您不能使用常规排序函数指定偏序并获得排序输出。相反,您需要实现拓扑排序。这是对您的数据的简单实现(除了我删除了重复的“密码”条目)。package mainimport "fmt"type Stmt struct { Name string After []string}func topSort(ss []Stmt) []string { after := map[string][]string{} // things that must come after counts := map[string]int{} // number unsatified preconditions zc := map[string]struct{}{} // things with zero count for _, s := range ss { for _, a := range s.After { after[a] = append(after[a], s.Name) } counts[s.Name] = len(s.After) if len(s.After) == 0 { zc[s.Name] = struct{}{} } } r := []string{} for len(zc) > 0 { for n := range zc { r = append(r, n) for _, a := range after[n] { counts[a]-- if counts[a] == 0 { zc[a] = struct{}{} } } delete(zc, n) } } return r}func main() { stmts := []Stmt{ {Name: "app", After: []string{"app_user"}}, {Name: "billingplan", After: []string{}}, {Name: "campaign", After: []string{"app_user"}}, {Name: "campaign_app", After: []string{"campaign", "app"}}, {Name: "campaign_ip", After: []string{"campaign", "ip"}}, {Name: "campaign_operator", After: []string{"campaign", "operator"}}, {Name: "campaign_sponsor", After: []string{"campaign", "sponsor"}}, {Name: "campaign_subscriberfilter", After: []string{"campaign", "subscriber_filters"}}, {Name: "campaign_url", After: []string{"campaign", "url"}}, {Name: "contentpartner", After: []string{"app_user"}}, {Name: "filter_criteria", After: []string{"campaign", "subscriber_filters"}}, {Name: "ip", After: []string{"app_user"}}, {Name: "mobile_registered", After: []string{"campaign", "app"}}, {Name: "operator", After: []string{}}, {Name: "passwords", After: []string{"app_user"}}, {Name: "publish_package", After: []string{}}, {Name: "role", After: []string{}}, {Name: "sponsor", After: []string{"app_user"}}, {Name: "subscriber_dbs", After: []string{}}, {Name: "subscriber_filters", After: []string{"subscriber_dbs"}}, {Name: "timezone", After: []string{}}, {Name: "url", After: []string{"app_user"}}, {Name: "app_user", After: []string{}}, {Name: "user_role", After: []string{"app_user", "role"}}, } r := topSort(stmts) for _, s := range r { fmt.Println(s) }}
慕斯王
正如匿名者所提到的,你需要一个拓扑排序。Tarjan 的强连通分量算法具有以反向拓扑排序顺序返回 SCC 的特性。这意味着它可以用作拓扑排序算法。这是基于维基百科伪代码的 Tarjan 算法的实现(可在此处运行,最初由我发布到 golang-nuts 列表)(更普遍地在此处实现,但使用基本相同的底层代码):package mainimport ( "fmt" "log")type Stmt struct { Name string After []string}func main() { stmts := []Stmt{ {Name: "app", After: []string{"app_user"}}, {Name: "billingplan", After: []string{}}, {Name: "campaign", After: []string{"app_user"}}, {Name: "campaign_app", After: []string{"campaign", "app"}}, {Name: "campaign_ip", After: []string{"campaign", "ip"}}, {Name: "campaign_operator", After: []string{"campaign", "operator"}}, {Name: "campaign_sponsor", After: []string{"campaign", "sponsor"}}, {Name: "campaign_subscriberfilter", After: []string{"campaign", "subscriber_filters"}}, {Name: "campaign_url", After: []string{"campaign", "url"}}, {Name: "contentpartner", After: []string{"app_user"}}, {Name: "filter_criteria", After: []string{"campaign", "subscriber_filters"}}, {Name: "ip", After: []string{"app_user"}}, {Name: "mobile_registered", After: []string{"campaign", "app"}}, {Name: "operator", After: []string{}}, {Name: "passwords", After: []string{"app_user"}}, {Name: "publish_package", After: []string{}}, {Name: "role", After: []string{}}, {Name: "passwords", After: []string{"app_user"}}, {Name: "sponsor", After: []string{"app_user"}}, {Name: "subscriber_dbs", After: []string{}}, {Name: "subscriber_filters", After: []string{"subscriber_dbs"}}, {Name: "timezone", After: []string{}}, {Name: "url", After: []string{"app_user"}}, {Name: "app_user", After: []string{}}, {Name: "user_role", After: []string{"app_user", "role"}}, } g := make(graph) for _, s := range stmts { g[s.Name] = after(s.After) } sorted, err := topoSort(g) if err != nil { log.Fatalf("could not sort: %v", err) } for _, s := range sorted { fmt.Println(s) }}func topoSort(g graph) ([]string, error) { sccs := tarjanSCC(g) sorted := make([]string, len(sccs)) for i, s := range sccs { if len(s) != 1 { return nil, fmt.Errorf("found directed cycle: %q", s) } sorted[i] = s[0] } return sorted, nil}// graph is an edge list representation of a directed graph.type graph map[string]set// set is an string set.type set map[string]struct{}func after(i []string) set { if len(i) == 0 { return nil } s := make(set) for _, v := range i { s[v] = struct{}{} } return s}// tarjanSCC returns a the strongly connected components of the// directed graph g.func tarjanSCC(g graph) [][]string { t := tarjan{ g: g, indexTable: make(map[string]int, len(g)), lowLink: make(map[string]int, len(g)), onStack: make(map[string]bool, len(g)), } for v := range t.g { if t.indexTable[v] == 0 { t.strongconnect(v) } } return t.sccs}// tarjan implements Tarjan's strongly connected component finding// algorithm. The implementation is from the pseudocode at//// http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm//type tarjan struct { g graph index int indexTable map[string]int lowLink map[string]int onStack map[string]bool stack []string sccs [][]string}// strongconnect is the strongconnect function described in the// wikipedia article.func (t *tarjan) strongconnect(v string) { // Set the depth index for v to the smallest unused index. t.index++ t.indexTable[v] = t.index t.lowLink[v] = t.index t.stack = append(t.stack, v) t.onStack[v] = true // Consider successors of v. for w := range t.g[v] { if t.indexTable[w] == 0 { // Successor w has not yet been visited; recur on it. t.strongconnect(w) t.lowLink[v] = min(t.lowLink[v], t.lowLink[w]) } else if t.onStack[w] { // Successor w is in stack s and hence in the current SCC. t.lowLink[v] = min(t.lowLink[v], t.indexTable[w]) } } // If v is a root node, pop the stack and generate an SCC. if t.lowLink[v] == t.indexTable[v] { // Start a new strongly connected component. var ( scc []string w string ) for { w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1] t.onStack[w] = false // Add w to current strongly connected component. scc = append(scc, w) if w == v { break } } // Output the current strongly connected component. t.sccs = append(t.sccs, scc) }}func min(a, b int) int { if a < b { return a } return b}请注意,重复运行此代码不会产生相同的严格输出顺序,因为许多路径相对于彼此并不是绝对可排序的(这不会显示在操场上,因为结果已被缓存 - 您可以看到这个尽管通过包装对 tarjanSCC 的调用)。虽然它可能是更容易直接实现拓扑排序,使用的Tarjan的SCC算法,我们能够找到一种失败的原因,例如这里(CF使用相同的数据在这里)。