// Package diagram provide diagram data structure and operation package diagram import ( "fmt" "github.com/gofrs/uuid" ) var GlobalTree *MultiBranchTreeNode // MultiBranchTreeNode represents a topological structure using an multi branch tree type MultiBranchTreeNode struct { ID uuid.UUID // 节点唯一标识 Parent *MultiBranchTreeNode // 指向父节点的指针 Children []*MultiBranchTreeNode // 指向所有子节点的指针切片 } func NewMultiBranchTree(id uuid.UUID) *MultiBranchTreeNode { return &MultiBranchTreeNode{ ID: id, Children: make([]*MultiBranchTreeNode, 0), } } func (n *MultiBranchTreeNode) AddChild(child *MultiBranchTreeNode) { child.Parent = n n.Children = append(n.Children, child) } func (n *MultiBranchTreeNode) RemoveChild(childID uuid.UUID) bool { for i, child := range n.Children { if child.ID == childID { n.Children = append(n.Children[:i], n.Children[i+1:]...) child.Parent = nil return true } } return false } func (n *MultiBranchTreeNode) FindNodeByID(id uuid.UUID) *MultiBranchTreeNode { if n.ID == id { return n } for _, child := range n.Children { if found := child.FindNodeByID(id); found != nil { return found } } return nil } func (n *MultiBranchTreeNode) PrintTree(level int) { for i := 0; i < level; i++ { fmt.Print(" ") } fmt.Printf("-ID: %s\n", n.ID) for _, child := range n.Children { child.PrintTree(level + 1) } } // FindPath returns the ordered node sequence from startID to endID using the // supplied nodeMap for O(1) lookup. It walks each node up to the root to find // the LCA, then stitches the two half-paths together. // Returns nil when either node is absent from nodeMap or no path exists. func FindPath(startID, endID uuid.UUID, nodeMap map[uuid.UUID]*MultiBranchTreeNode) []*MultiBranchTreeNode { startNode, ok := nodeMap[startID] if !ok { return nil } endNode, ok := nodeMap[endID] if !ok { return nil } // collect ancestors (inclusive) from a node up to the root sentinel ancestors := func(n *MultiBranchTreeNode) []*MultiBranchTreeNode { var chain []*MultiBranchTreeNode for n != nil { chain = append(chain, n) n = n.Parent } return chain } startChain := ancestors(startNode) // [start, ..., root] endChain := ancestors(endNode) // [end, ..., root] // index startChain by ID for fast LCA detection startIdx := make(map[uuid.UUID]int, len(startChain)) for i, node := range startChain { startIdx[node.ID] = i } // find LCA: first node in endChain that also appears in startChain lcaEndPos := -1 lcaStartPos := -1 for i, node := range endChain { if j, found := startIdx[node.ID]; found { lcaEndPos = i lcaStartPos = j break } } if lcaEndPos < 0 { return nil // disconnected } // path = startChain[0..lcaStartPos] reversed + endChain[lcaEndPos..0] reversed path := make([]*MultiBranchTreeNode, 0, lcaStartPos+lcaEndPos+1) for i := 0; i <= lcaStartPos; i++ { path = append(path, startChain[i]) } // append end-side (skip LCA to avoid duplication), reversed for i := lcaEndPos - 1; i >= 0; i-- { path = append(path, endChain[i]) } return path }