正在显示
2 个修改的文件
包含
248 行增加
和
0 行删除
pkg/infrastructure/utils/tree.go
0 → 100644
1 | +package utils | ||
2 | + | ||
3 | +type TreeNode interface { | ||
4 | + PID() string | ||
5 | + ID() string | ||
6 | +} | ||
7 | + | ||
8 | +type Tree struct { | ||
9 | + Node TreeNode `json:"node"` | ||
10 | + Nodes []*Tree `json:"nodes"` | ||
11 | +} | ||
12 | + | ||
13 | +func NewTree(nodes []TreeNode) *Tree { | ||
14 | + var tree = &Tree{ | ||
15 | + Node: nil, | ||
16 | + Nodes: make([]*Tree, 0), | ||
17 | + } | ||
18 | + for i := range nodes { | ||
19 | + match := traverseAdd(tree, nodes[i]) | ||
20 | + if !match { | ||
21 | + tree.Nodes = append(tree.Nodes, newTree(nodes[i])) | ||
22 | + } | ||
23 | + } | ||
24 | + return tree | ||
25 | +} | ||
26 | + | ||
27 | +func newTree(node TreeNode) *Tree { | ||
28 | + return &Tree{ | ||
29 | + Node: node, | ||
30 | + Nodes: make([]*Tree, 0), | ||
31 | + } | ||
32 | +} | ||
33 | + | ||
34 | +func (tree *Tree) Root() TreeNode { | ||
35 | + if tree.Node != nil { | ||
36 | + return tree.Node | ||
37 | + } | ||
38 | + if len(tree.Nodes) > 0 { | ||
39 | + return tree.Nodes[0].Node | ||
40 | + } | ||
41 | + return nil | ||
42 | +} | ||
43 | + | ||
44 | +// TreeNodePaths returns all the parents of the current node 1->5->7 , use time n*O(n)(need performance optimization) | ||
45 | +func (tree *Tree) TreeNodePaths(node TreeNode) []TreeNode { | ||
46 | + treeNode := node | ||
47 | + result := make([]TreeNode, 0) | ||
48 | + for { | ||
49 | + if treeNode == nil { | ||
50 | + break | ||
51 | + } | ||
52 | + tmp := tree.find(treeNode, func(a, b TreeNode) bool { | ||
53 | + if a.ID() == b.PID() { | ||
54 | + return true | ||
55 | + } | ||
56 | + return false | ||
57 | + }) | ||
58 | + result = append(result, treeNode) | ||
59 | + if tmp == nil { | ||
60 | + break | ||
61 | + } | ||
62 | + treeNode = tmp.Node | ||
63 | + } | ||
64 | + reserveResult := make([]TreeNode, 0) | ||
65 | + for i := len(result) - 1; i >= 0; i-- { | ||
66 | + reserveResult = append(reserveResult, result[i]) | ||
67 | + } | ||
68 | + return reserveResult | ||
69 | +} | ||
70 | + | ||
71 | +// Add adds a node to the first matching parent tree if add success it return true | ||
72 | +func (tree *Tree) Add(node TreeNode) bool { | ||
73 | + return traverseAdd(tree, node) | ||
74 | +} | ||
75 | + | ||
76 | +// AllChildNode returns all child nodes under Node, including itself | ||
77 | +func (tree *Tree) AllChildNode(node TreeNode) []TreeNode { | ||
78 | + treeNode := tree.find(node, nil) | ||
79 | + if treeNode == nil { | ||
80 | + return []TreeNode{} | ||
81 | + } | ||
82 | + return tree.allChildNode(treeNode, nil) | ||
83 | +} | ||
84 | + | ||
85 | +//AllLeafNode returns all leaf node under Node ,if node is nil returns all leaf node under tree | ||
86 | +func (tree *Tree) AllLeafNode(node TreeNode) []TreeNode { | ||
87 | + treeNode := tree | ||
88 | + if node != nil { | ||
89 | + treeNode = tree.find(node, nil) | ||
90 | + } | ||
91 | + if treeNode == nil { | ||
92 | + return []TreeNode{} | ||
93 | + } | ||
94 | + return tree.allChildNode(treeNode, func(node *Tree) bool { | ||
95 | + if len(node.Nodes) == 0 { | ||
96 | + return true | ||
97 | + } | ||
98 | + return false | ||
99 | + }) | ||
100 | +} | ||
101 | + | ||
102 | +// find query the node in this tree | ||
103 | +func (tree *Tree) find(node TreeNode, compared func(a, b TreeNode) bool) *Tree { | ||
104 | + var stack []*Tree | ||
105 | + stack = append(stack, tree) | ||
106 | + var find *Tree | ||
107 | + for { | ||
108 | + if len(stack) == 0 { | ||
109 | + break | ||
110 | + } | ||
111 | + pop := stack[0] | ||
112 | + stack = stack[1:] | ||
113 | + stack = append(stack, pop.Nodes...) | ||
114 | + if pop == nil || pop.Node == nil { | ||
115 | + continue | ||
116 | + } | ||
117 | + if compared != nil { | ||
118 | + if compared(pop.Node, node) { | ||
119 | + find = pop | ||
120 | + break | ||
121 | + } | ||
122 | + continue | ||
123 | + } | ||
124 | + if pop.Node.ID() == node.ID() { | ||
125 | + find = pop | ||
126 | + break | ||
127 | + } | ||
128 | + } | ||
129 | + return find | ||
130 | +} | ||
131 | + | ||
132 | +// allChildNode 返回treeNode下所有子节点 | ||
133 | +func (tree *Tree) allChildNode(treeNode *Tree, filter func(node *Tree) bool) []TreeNode { | ||
134 | + var stack []*Tree | ||
135 | + stack = append(stack, treeNode) | ||
136 | + var res []TreeNode | ||
137 | + for { | ||
138 | + if len(stack) == 0 { | ||
139 | + break | ||
140 | + } | ||
141 | + pop := stack[0] | ||
142 | + stack = stack[1:] | ||
143 | + stack = append(stack, pop.Nodes...) | ||
144 | + if filter != nil && !filter(pop) { | ||
145 | + continue | ||
146 | + } | ||
147 | + res = append(res, pop.Node) | ||
148 | + } | ||
149 | + return res | ||
150 | +} | ||
151 | + | ||
152 | +// traverseAdd 递归添加 | ||
153 | +// | ||
154 | +// tree 当前树 | ||
155 | +// node 判断的节点 | ||
156 | +func traverseAdd(tree *Tree, node TreeNode) bool { | ||
157 | + list := tree.Nodes | ||
158 | + var match bool = false | ||
159 | + for i := range list { | ||
160 | + id, pid := list[i].Node.ID(), node.PID() | ||
161 | + if pid == id { | ||
162 | + list[i].Nodes = append(list[i].Nodes, newTree(node)) | ||
163 | + return true | ||
164 | + } | ||
165 | + if match || traverseAdd(list[i], node) { | ||
166 | + match = true | ||
167 | + break | ||
168 | + } | ||
169 | + } | ||
170 | + return match | ||
171 | +} |
pkg/infrastructure/utils/tree_test.go
0 → 100644
1 | +package utils | ||
2 | + | ||
3 | +import ( | ||
4 | + "github.com/stretchr/testify/assert" | ||
5 | + "strconv" | ||
6 | + "testing" | ||
7 | +) | ||
8 | + | ||
9 | +func Test_Tree(t *testing.T) { | ||
10 | + table := []struct { | ||
11 | + Input []TreeNode | ||
12 | + Text string | ||
13 | + Except []string | ||
14 | + Except2 []string | ||
15 | + }{ | ||
16 | + { | ||
17 | + Input: []TreeNode{ | ||
18 | + &st{Id: 1, Pid: 0}, | ||
19 | + &st{Id: 2, Pid: 1}, &st{Id: 3, Pid: 1}, &st{Id: 4, Pid: 1}, | ||
20 | + &st{Id: 5, Pid: 3}, | ||
21 | + &st{Id: 6, Pid: 5}, &st{Id: 7, Pid: 5}}, | ||
22 | + Text: ` | ||
23 | +树形结构: | ||
24 | + 1 | ||
25 | +2 3 4 | ||
26 | + 5 | ||
27 | + 6 7 | ||
28 | +`, | ||
29 | + Except: []string{"5", "6", "7"}, | ||
30 | + Except2: []string{"2", "4", "6", "7"}, | ||
31 | + }, | ||
32 | + } | ||
33 | + | ||
34 | + for i := range table { | ||
35 | + tree := NewTree(table[i].Input) | ||
36 | + out := tree.AllChildNode(&st{Id: 5, Pid: 3}) | ||
37 | + var res []string = treeNodeResults(out) | ||
38 | + assert.Equal(t, res, table[i].Except) | ||
39 | + | ||
40 | + out = tree.AllLeafNode(nil) //tree.Root() | ||
41 | + res = treeNodeResults(out) | ||
42 | + assert.Equal(t, res, table[i].Except2) | ||
43 | + | ||
44 | + root := tree.Root() | ||
45 | + assert.Equal(t, root.ID(), "1") | ||
46 | + | ||
47 | + //tree.Add(&st{Id:10,Pid: 7}) | ||
48 | + // | ||
49 | + //out = tree.AllLeafNode(tree.Root()) | ||
50 | + //res = treeNodeResults(out) | ||
51 | + //assert.Equal(t, res, []string{"2", "4", "6", "10"}) | ||
52 | + | ||
53 | + out = tree.TreeNodePaths(&st{Id: 7, Pid: 5}) | ||
54 | + res = treeNodeResults(out) | ||
55 | + assert.Equal(t, res, []string{"1", "3", "5", "7"}) | ||
56 | + } | ||
57 | +} | ||
58 | + | ||
59 | +type st struct { | ||
60 | + Id int | ||
61 | + Pid int | ||
62 | +} | ||
63 | + | ||
64 | +func (t *st) PID() string { | ||
65 | + return strconv.Itoa(t.Pid) | ||
66 | +} | ||
67 | +func (t *st) ID() string { | ||
68 | + return strconv.Itoa(t.Id) | ||
69 | +} | ||
70 | + | ||
71 | +func treeNodeResults(nodes []TreeNode) []string { | ||
72 | + var res []string | ||
73 | + for i := range nodes { | ||
74 | + res = append(res, nodes[i].ID()) | ||
75 | + } | ||
76 | + return res | ||
77 | +} |
-
请 注册 或 登录 后发表评论