女生做蛋糕甜品屋宝宝
105.50M · 2026-04-17
在后端开发中,我们经常需要处理层级数据,比如部门结构、菜单权限或商品分类。数据库通常存储为扁平的列表(List),而前端往往需要嵌套的树形结构(Tree)。
探讨一种基于HashMap的高效算法,实现List到Tree以及Tree到List的双向转换。
两种数据结构:
// 扁平节点
class Node {
int id;
int pid;
// 构造函数与Getter省略
}
// 树形节点
class Tree {
int id;
List<Tree> children;
// 构造函数与Getter省略
}
核心思路是利用HashMap的空间换时间策略,将时间复杂度降低到O(n)。
public static Tree toTree(List<Node> nodes) {
if (nodes == null || nodes.isEmpty()) {
return null;
}
// 1. 初始化Map并寻找根节点ID
var map = new HashMap<Integer, Tree>(nodes.size() + 1);
var rid = Integer.MAX_VALUE;
for (var n : nodes) {
if (n == null) {
continue;
}
// 寻找最小的pid作为根节点ID
if (n.getPid() < rid) {
rid = n.getPid();
}
// 将节点放入Map
map.put(n.getId(), new Tree(n.getId()));
}
if (map.isEmpty()) {
return null;
}
// 确保根节点也在Map中
map.putIfAbsent(rid, new Tree(rid));
var root = map.get(rid);
// 2. 组装树形结构
for (var n : nodes) {
if (n == null) {
continue;
}
var child = map.get(n.getId());
var parent = map.get(n.getPid());
if (parent != null) {
parent.getChildren().add(child);
}
}
return root;
}
这是一个典型的深度优先搜索(DFS)过程,通过递归遍历树的每一层。
public static List<Node> toNodes(Tree tree) {
var data = new ArrayList<Node>();
if (tree == null) {
return data;
}
appendChildren(data, tree);
return data;
}
private static void appendChildren(List<Node> data, Tree tree) {
var children = tree.children;
if (children.isEmpty()) {
return;
}
// 遍历子节点,将其转换为扁平Node并加入列表
for (var c : children) {
data.add(new Node(c.id, tree.id));
// 递归处理下一级
appendChildren(data, c);
}
}
通过一个具体的树形结构来验证代码的正确性。
0
/
1 2
/
3 4
{
"id": 0,
"children": [
{
"id": 1,
"children": [
{
"id": 3,
"children": []
},
{
"id": 4,
"children": []
}
]
},
{
"id": 2,
"children": []
}
]
}
[
{
"id": 1,
"pid": 0
},
{
"id": 3,
"pid": 1
},
{
"id": 4,
"pid": 1
},
{
"id": 2,
"pid": 0
}
]