golang 實現菜單樹的生成,包括菜單節點的選中狀態、半選中狀態,菜單的搜索。
1 該包提供兩個方法根接口
1.1 GenerateTree(nodes, selectedNodes []INode) (trees []Tree)
GenerateTree 自定義的結構體實現 INode 接口后調用此方法生成樹結構。
1.2 FindRelationNode(nodes, allNodes []INode) (respNodes []INode)
FindRelationNode 在 allTree 中查詢 nodes 中節點的所有父子節點 返回 respNodes(包含 nodes , 跟其所有父子節點)
1.3 接口 INode
// ConvertToINodeArray 其他的結構體想要生成菜單樹,直接實現這個接口
type INode interface {
// GetTitle 獲取顯示名字
GetTitle() string
// GetId獲取id
GetId() int
// GetFatherId 獲取父id
GetFatherId() int
// GetData 獲取附加數據
GetData() interface{}
// IsRoot 判斷當前節點是否是頂層根節點
IsRoot() bool
}
2 使用
go get github.com/azhengyongqin/golang-tree-menu
2.1 定義自己的菜單結構體并且實現接口 INode
// 定義我們自己的菜單對象
type SystemMenu struct {
Id int `json:"id"` //id
FatherId int `json:"father_id"` //上級菜單id
Name string `json:"name"` //菜單名
Route string `json:"route"` //頁面路徑
Icon string `json:"icon"` //圖標路徑
}
func (s SystemMenu) GetTitle() string {
return s.Name
}
func (s SystemMenu) GetId() int {
return s.Id
}
func (s SystemMenu) GetFatherId() int {
return s.FatherId
}
func (s SystemMenu) GetData() interface{} {
return s
}
func (s SystemMenu) IsRoot() bool {
// 這里通過FatherId等于0 或者 FatherId等于自身Id表示頂層根節點
return s.FatherId == 0 || s.FatherId == s.Id
}
2.2 實現一個將自定義結構體SystemMenu 數組轉換成 INode 數組的方法
type SystemMenus []SystemMenu
// ConvertToINodeArray 將當前數組轉換成父類 INode 接口 數組
func (s SystemMenus) ConvertToINodeArray() (nodes []INode) {
for _, v := range s {
nodes = append(nodes, v)
}
return
}
3 測試效果
3.1 添加測試數據
// 模擬獲取數據庫中所有菜單,在其它所有的查詢中,也是首先將數據庫中所有數據查詢出來放到數組中,
// 后面的遍歷遞歸,都在這個 allMenu中進行,而不是在數據庫中進行遞歸查詢,減小數據庫壓力。
allMenu := []SystemMenu{
{Id: 1, FatherId: 0, Name: "系統總覽", Route: "/systemOverview", Icon: "icon-system"},
{Id: 2, FatherId: 0, Name: "系統配置", Route: "/systemConfig", Icon: "icon-config"},
{Id: 3, FatherId: 1, Name: "資產", Route: "/asset", Icon: "icon-asset"},
{Id: 4, FatherId: 1, Name: "動環", Route: "/pe", Icon: "icon-pe"},
{Id: 5, FatherId: 2, Name: "菜單配置", Route: "/menuConfig", Icon: "icon-menu-config"},
{Id: 6, FatherId: 3, Name: "設備", Route: "/device", Icon: "icon-device"},
{Id: 7, FatherId: 3, Name: "機柜", Route: "/device", Icon: "icon-device"},
}
3.2 生成完全樹
// 生成完全樹
resp := GenerateTree(SystemMenus.ConvertToINodeArray(allMenu), nil)
bytes, _ := json.MarshalIndent(resp, "", "\t")
fmt.Println(string(bytes))
[
{
"title": "系統總覽",
"leaf": false,
"checked": false,
"partial_selected": false,
"children": [
{
"title": "資產",
"leaf": false,
"checked": false,
"partial_selected": false,
"children": [
{
"title": "設備",
"leaf": true,
"checked": false,
"partial_selected": false,
"children": null
},
{
"title": "機柜",
"leaf": true,
"checked": false,
"partial_selected": false,
"children": null
}
]
},
{
"title": "動環",
"leaf": true,
"checked": false,
"partial_selected": false,
"children": null
}
]
},
{
"title": "系統配置",
"leaf": false,
"checked": false,
"partial_selected": false,
"children": [
{
"title": "菜單配置",
"leaf": true,
"checked": false,
"partial_selected": false,
"children": null
}
]
}
]

3.3 帶選中狀態和半選中狀態的樹
// 模擬選中 '資產' 菜單
selectedNode := []SystemMenu{allMenu[2]}
resp = GenerateTree(SystemMenus.ConvertToINodeArray(allMenu), SystemMenus.ConvertToINodeArray(selectedNode))
bytes, _ = json.Marshal(resp)
fmt.Println(string(pretty.Color(pretty.PrettyOptions(bytes, pretty.DefaultOptions), nil)))

[
{
"title": "系統總覽",
"leaf": false,
"checked": false,
"partial_selected": true,
"children": [
{
"title": "資產",
"leaf": false,
"checked": true,
"partial_selected": false,
"children": [
{
"title": "設備",
"leaf": true,
"checked": true,
"partial_selected": false,
"children": null
},
{
"title": "機柜",
"leaf": true,
"checked": true,
"partial_selected": false,
"children": null
}
]
},
{
"title": "動環",
"leaf": true,
"checked": false,
"partial_selected": false,
"children": null
}
]
},
{
"title": "系統配置",
"leaf": false,
"checked": false,
"partial_selected": false,
"children": [
{
"title": "菜單配置",
"leaf": true,
"checked": false,
"partial_selected": false,
"children": null
}
]
}
]
3.4 模擬查詢某個節點,然后生成樹
// 模擬從數據庫中查詢出 '設備'
device := []SystemMenu{allMenu[5]}
// 查詢 `設備` 的所有父節點
respNodes := FindRelationNode(SystemMenus.ConvertToINodeArray(device), SystemMenus.ConvertToINodeArray(allMenu))
resp = GenerateTree(respNodes, nil)
bytes, _ = json.Marshal(resp)
fmt.Println(string(pretty.Color(pretty.PrettyOptions(bytes, pretty.DefaultOptions), nil)))

[
{
"title": "系統總覽",
"leaf": false,
"checked": false,
"partial_selected": false,
"children": [
{
"title": "資產",
"leaf": false,
"checked": false,
"partial_selected": false,
"children": [
{
"title": "設備",
"leaf": true,
"checked": false,
"partial_selected": false,
"children": null
}
]
}
]
}
]
源碼地址:https://github.com/azhengyongqin/golang-tree-menu
補充:golang實現prim算法,計算最小生成樹
1、題目描述
給定一個n個點m條邊的無向圖,圖中可能存在重邊和自環,邊權可能為負數。
求最小生成樹的樹邊權重之和,如果最小生成樹不存在則輸出impossible。
給定一張邊帶權的無向圖G=(V, E),其中V表示圖中點的集合,E表示圖中邊的集合,n=|V|,m=|E|。
由V中的全部n個頂點和E中n-1條邊構成的無向連通子圖被稱為G的一棵生成樹,其中邊的權值之和最小的生成樹被稱為無向圖G的最小生成樹。
輸入格式
第一行包含兩個整數n和m。
接下來m行,每行包含三個整數u,v,w,表示點u和點v之間存在一條權值為w的邊。
輸出格式
共一行,若存在最小生成樹,則輸出一個整數,表示最小生成樹的樹邊權重之和,如果最小生成樹不存在則輸出impossible。
2、數據
數據范圍
1≤n≤500,
1≤m≤105,
圖中涉及邊的邊權的絕對值均不超過10000。
輸入樣例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
輸出樣例:
6
數據圖
1、初始所有點的距離為正無窮,就是代碼中的0x3f3f3f3f等于1061109567

2、以第一個點為最初點,綠色表示選中,進入到最小生成樹中

3、以第一個更新其他與之連通的點的距離

4、依次迭代 
5、最后的最小生成樹

3、樸素prim算法步驟時間復雜度O(n^2)
1、先初始化所有點距離為正無窮
2、迭代n次,依次用到集合的最小點更新剩余點距離
3、將已經確定的點加入到st集合中,st數組為一個bool類型
4、代碼實現
/*
該圖是稠密圖,使用鄰接矩陣
*/
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
const (
N = 510
INF = 0x3f3f3f3f
)
var (
n, m int
dist [N]int
g [N][N]int
st [N]bool
)
func readLine(r *bufio.Reader) []int {
s, _ := r.ReadString('\n')
ss := strings.Fields(s)
res := make([]int, len(ss))
for i, v := range ss {
res[i], _ = strconv.Atoi(v)
}
return res
}
func prim() int {
// 初始化距離集合 dist
for i := 0; i N; i++ {
dist[i] = 0x3f3f3f3f
}
// 迭代n次
res := 0 //res 存儲最小生成樹的大小即邊的長度總和
for i := 0; i n; i++ {
// 找到集合外距離最短的點
t := -1
for j := 1; j = n; j++ {
if !st[j] (t == -1 || dist[t] > dist[j]) {
t = j
}
}
// 迭代結束,此時的t就是距離最小點
// 情況一:圖上的點不連通,不能組成最小生成樹
if i > 0 dist[t] == INF {
return INF
} // 如果不是第一個點并且最小店的距離是正無窮,則表示圖是不連通的
if i > 0 {
res += dist[t]
} // 如果不是第一個點,這個t就表示當前點到集合某一個點的最小距離
// 用最小距離點更新其他跟 "現階段形成的生成樹" 的最短距離,
//注意更新的順序,自環是不應該被加到最小生成樹,所以,為了避免自環加入最小生成樹,提前更新res
for j := 1; j = n; j++ {
dist[j] = min(dist[j], g[t][j]) // 此步驟注意是dijkstra的區別,
}
st[t] = true
}
return res
}
func min(a, b int) int {
if a >= b {
return b
} else {
return a
}
}
func main() {
r := bufio.NewReader(os.Stdin)
input := readLine(r)
n, m = input[0], input[1]
//fmt.Scanf("%d%d\n", n, m)
// 初始化距離
for i := 0; i N; i++ {
for j := 0; j N; j++ {
if i == j {
g[i][j] = 0
} else {
g[i][j] = 0x3f3f3f3f
}
}
}
//
for m > 0 {
m--
in := readLine(r)
a, b, c := in[0], in[1], in[2] //輸入
g[a][b] = min(g[a][b], c)
g[b][a] = g[a][b] // 無向圖
}
t := prim()
if t == INF {
fmt.Println("impossible")
} else {
fmt.Println(t)
}
}
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
您可能感興趣的文章:- golang通過遞歸遍歷生成樹狀結構的操作
- goland 恢復已更改文件的操作
- goland 清除所有的默認設置操作
- Golang 的defer執行規則說明
- golang 的string與[]byte轉換方式