modelRT/diagram/multi_branch_tree.go

126 lines
3.1 KiB
Go

// 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
}