liujie
2026-05-16 539fa09b48cfe03c573284381cfba4ba092e8007
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package com.estate.expert.admin.utils.tree.helper;
 
import com.estate.expert.admin.utils.tree.TreeNode;
import lombok.extern.slf4j.Slf4j;
 
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
 
/**
 * 树的构建器
 *
 * 主要用于从一棵树生成另外一个相同结构的树
 *
 */
@Slf4j
public class TreeHelper {
 
    /**
     * 构建新树
     *
     * 采用深度优先遍历
     * 自上而下遍历
     * 适合层级树,可以较快的裁剪掉不需要的路径
     */
    public static <T, K> TreeNode<K> convertTree(TreeNode<T> node, NodeConvertor<T, K> convertor, NodeFilter<T> filter) {
        if(!filter.shouldAdd(node))
            return null;
 
        TreeNode<K> newNode = cloneNode(node);
        newNode.setData(convertor.convert(node, newNode));
 
        for (TreeNode<T> child : node.getChildren().values()) {
            var newChild = convertTree(child, convertor, filter);
            if(newChild != null)
                newNode.addChild(newChild);
        }
        return newNode;
    }
 
    /**
     * 单纯转换树,不进行裁剪
     */
    public static <T, K> TreeNode<K> convertTree(TreeNode<T> node, NodeConvertor<T, K> convertor) {
        return convertTree(node ,convertor, (n) -> true);
    }
 
    /**
     * 裁剪树
     */
    public static <T> TreeNode<T> pruneTree(TreeNode<T> node, NodeFilter<T> filter) {
        return convertTree(node , (n, newNode)->{
            return n.getData();
        }, filter);
    }
 
    /**
     * 按层级裁剪树
     */
    public static <T> TreeNode<T> pruneTreeByLevels(TreeNode<T> node, List<Integer> levels) {
        // 创建一个新的空节点作为新树的根节点
        TreeNode<T> newRoot = cloneNode(node);
        newRoot.setData(node.getData());
 
        // 创建一个栈,用于存放当前正在处理的节点路径
        Deque<TreeNode<T>> path = new ArrayDeque<>();
        path.push(newRoot);
 
        // 使用深度优先搜索遍历原始树
        dfsPrune(node, 0, levels, path);
 
        return newRoot;
    }
 
    /**
     * 将路径转换为id
     * @param path 路径
     * @return id
     */
    private static int pathToId(String path){
        var parts = path.split("/");
        int id = 0;
        for(var part : parts){
            if(part.isEmpty())
                continue;
            id = id * 1000 + Integer.parseInt(part);
        }
        return id;
    }
 
    /**
     * 深度优先搜索裁剪树
     * @param node 节点
     * @param level 层级
     * @param levels 层级列表
     * @param path 路径
     */
    private static <T> void dfsPrune(TreeNode<T> node, int level, List<Integer> levels, Deque<TreeNode<T>> path) {
        if (levels.contains(level)) {
            // 如果当前层级在指定的层级列表中,就复制当前节点到新树中
            TreeNode<T> newNode = cloneNode(node);
 
            // 由于存在跨层合并的情况,所以需要重新计算id,避免id冲突
            var fullId = pathToId(node.getPath());
            newNode.setId(fullId);
            newNode.setData(node.getData());
            path.peek().addChild(newNode);
            path.push(newNode);
        }
 
        // 遍历子节点
        for (TreeNode<T> child : node.getChildren().values()) {
            dfsPrune(child, level + 1, levels, path);
        }
 
        if (levels.contains(level)) {
            // 如果当前层级在指定的层级列表中,就从路径中移除当前节点
            path.pop();
        }
    }
 
    /**
     * 自下而上遍历树
     * 用于计算结果求解,需要先求解子节点的结果
     */
    public static <T> void travel(TreeNode<T> node, NodeVisitor<T> visitor) {
        if(node == null)
            return;
        for (TreeNode<T> child : node.getChildren().values()) {
            travel(child, visitor);
        }
        visitor.visit(node);
    }
 
    /**
     * 广度优先遍历树
     * @param node 节点
     * @param visitor 访问器
     */
    public static <T> void bfsTravel(TreeNode<T> node, NodeVisitor<T> visitor){
        var queue = new java.util.LinkedList<TreeNode<T>>();
        queue.add(node);
 
        while (!queue.isEmpty()){
            var n = queue.poll();
            visitor.visit(n);
            queue.addAll(n.getChildren().values());
        }
    }
 
    /**
     * 打印树
     * @param node 节点
     */
    public static <T> void printTree(TreeNode<T> node){
        if(node == null)
            return;
        bfsTravel(node, (n) -> {
            log.info("{}", n);
        });
    }
 
    /**
     * 克隆节点
     * @param node 节点
     * @return 克隆节点
     */
    private static <T, K> TreeNode<K> cloneNode(TreeNode<T> node) {
        TreeNode<K> newNode = new TreeNode<>();
        newNode.setId(node.getId());
        newNode.setPath(node.getPath());
        newNode.setName(node.getName());
        return newNode;
    }
 
}