在数据表中,数据是以一条一条,这种扁平化的数据保存的,然后通过外键这种形式关联起来,如果是一个树形结构,在扁平化的一维列表中 是这个样子的,
假设数据保存在Mysql
的tmp
表,那么数据格式为:
DROP TABLE IF EXISTS `tmp`;
CREATE TABLE `tmp` (
`id` int NOT NULL AUTO_INCREMENT,
`pid` int NOT NULL DEFAULT '0',
`content` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3;
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (1, 0, '1');
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (2, 1, '1-2');
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (3, 1, '1-3');
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (4, 3, '1-3-4');
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (5, 0, '5');
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (6, 5, '5-6');
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (7, 6, '5-6-7');
INSERT INTO `tmp` (`id`, `pid`, `content`) VALUES (8, 6, '5-6-8');
-- 然后按顺序查询出来
mysql> SELECT * FROM tmp ORDER BY CONCAT(pid, '-', id) ASC;
+----+-----+---------+
| id | pid | content |
+----+-----+---------+
| 1 | 0 | 1 |
| 5 | 0 | 5 |
| 2 | 1 | 1-2 |
| 3 | 1 | 1-3 |
| 4 | 3 | 1-3-4 |
| 6 | 5 | 5-6 |
| 7 | 6 | 5-6-7 |
| 8 | 6 | 5-6-8 |
+----+-----+---------+
8 rows in set (0.01 sec)
扁平的为一维列表的树状结构
var items = []ItemType{
{Id: 1, Pid: 0, Content: "1"},
{Id: 2, Pid: 1, Content: "1-2"},
{Id: 3, Pid: 1, Content: "1-3"},
{Id: 4, Pid: 3, Content: "1-3-4"},
{Id: 5, Pid: 0, Content: "5"},
{Id: 6, Pid: 5, Content: "5-6"},
{Id:7 , Pid: 6, Content: "5-6-7"},
{Id: 8, Pid: 6, Content: "5-6-8"},
}
像这样的数据是可以保存在数据库中的,又通过外键的关联描述出了一个树状结构。那么把扁平的一维数据列表转化为树下状,大概是这样子:
package main
import (
"encoding/json"
"fmt"
)
type ItemType struct {
Content string
Pid int64
Id int64
}
type TreeItem struct {
ItemType
Children []*TreeItem
}
type IdMapTreeType map[int64]*TreeItem
func main() {
var items = []ItemType{
{Id: 1, Pid: 0, Content: "1"},
{Id: 2, Pid: 1, Content: "1-2"},
{Id: 3, Pid: 1, Content: "1-3"},
{Id: 4, Pid: 3, Content: "1-3-4"},
{Id: 5, Pid: 0, Content: "5"},
{Id: 6, Pid: 5, Content: "5-6"},
{Id:7 , Pid: 6, Content: "5-6-7"},
{Id: 8, Pid: 6, Content: "5-6-8"},
}
var tree []*TreeItem
idMapTreeItem := make(IdMapTreeType)
for _, item := range items {
var treeItem TreeItem
treeItem.Id = item.Id
treeItem.Pid = item.Pid
treeItem.Content = item.Content
// 根节点收集
if item.Pid == 0 {
tree = append(tree, &treeItem)
} else {
// 子节点收集
idMapTreeItem[item.Pid].Children = append(idMapTreeItem[item.Pid].Children, &treeItem)
}
// 把节点映射到map表
idMapTreeItem[item.Id] = &treeItem
}
jsonRes, _ := json.Marshal(tree)
fmt.Println(string(jsonRes))
}
列表转化为树结构
[
{
"Content": "1",
"Pid": 0,
"Id": 1,
"Children": [
{
"Content": "1-2",
"Pid": 1,
"Id": 2,
"Children": null
},
{
"Content": "1-3",
"Pid": 1,
"Id": 3,
"Children": [
{
"Content": "1-3-4",
"Pid": 3,
"Id": 4,
"Children": null
}
]
}
]
},
{
"Content": "5",
"Pid": 0,
"Id": 5,
"Children": [
{
"Content": "5-6",
"Pid": 5,
"Id": 6,
"Children": [
{
"Content": "5-6-7",
"Pid": 6,
"Id": 7,
"Children": null
},
{
"Content": "5-6-8",
"Pid": 6,
"Id": 8,
"Children": null
}
]
}
]
}
]
原理就是每一条数据在树中都有一个特定的位置,数据项是根节点则直接加入到根节点中, 并注册id
映射到它的内存地址(指针),
如果不是根节点,则通过它的上级pid
在映射表中把上级的内存地址拿到并把数据写进去,并也是一样把自己注册到映射表中。然后就还原出来了。
文章转载自《重学golang.算法.# 扁平数据列表转化为树状结构》