背景
最近在做业务的时候,发现有很多地方需要将数据库查询出来的 list 转为 tree 结构,基于面向对象的思想,想着做成一个通用的工具类,使用泛型,然后首先想到的是使用递归,O(n * n) 后面觉得递归的传统方式,时间复杂度过高,基于此进行优化,想到了一个线性规划的方式,进行数据扁平化转为线性复杂度 O(N)
接口限定
定义一个 ItreeNode 接口,该接口主要用来子类实现,自己定义父节点的规则,只要使用方自己实现 parent 方法,自己定义父节点的规则,这样就无需 id 和 parentId 必须提前限制类型了, string 或者对象都可以作为获取父节点的基准
import java.util.List;
/**
* 通过实现该接口的子类 自己定义parent的规则
* @param <T>
*/
public interface ITreeNode<T> {
T parent();
void setChildren(List<T> children);
List<T> getChildren();
}
第一版
使用 stream 进行流式编程加上栈结构,不使用递归的方式,但是每个节点都得循环里面 forEach 找到关联的子节点
时间复杂度 O(N*N)
public static <T extends ITreeNode<T>> List<T> list2Tree1(Collection<T> source) {
List<T> roots = source.stream().filter(node -> node.parent() == null).collect(Collectors.toList());
Stack<T> rootStack = roots.stream().collect(Collectors.toCollection(Stack::new));
while (!rootStack.isEmpty()) {
T node = rootStack.pop();
node.setChildren(
source.stream()
.filter(child -> Objects.equals(child.parent(), node))
.collect(Collectors.toList()));
rootStack.addAll(node.getChildren());
}
return roots;
}
这种方式当数据量大的时候,容易耗时,时间复杂度过高
第二版
使用线性规划,只需要提前将某个父节点的字节点关系映射缓存起来,后续直接通过父节点就能直接拿到子节点数据,只需要循环一次,时间复杂度变为线性 O(N)
public static <T extends ITreeNode<T>> List<T> list2Tree(Collection<T> source) {
Map<T, List<T>> relationMap = new HashMap<>();
source.forEach(node -> relationMap.computeIfAbsent(node.parent(), k -> new ArrayList<>()).add(node));
List<T> roots = relationMap.getOrDefault(null, Collections.emptyList());
Stack<T> rootStack = roots.stream().collect(Collectors.toCollection(Stack::new));
while (!rootStack.isEmpty()) {
T node = rootStack.pop();
List<T> childList = relationMap.getOrDefault(node, Collections.emptyList());
node.setChildren(childList);
rootStack.addAll(node.getChildren());
}
return roots;
}
测试
public static void main(String[] args) {
// List<TreeMember> list = new ArrayList<>();
// list.add(new TreeMember(1,null,"广东"));
// list.add(new TreeMember(2,1,"深圳"));
// list.add(new TreeMember(3,2,"南山"));
// list.add(new TreeMember(4,null,"广西"));
// list.add(new TreeMember(5,2,"龙华"));
// List<TreeMember> tree = list2Tree(list);
List<TreeMember2> list = new ArrayList<>();
String guangdong = UUID.randomUUID().toString();
String shenzhen = UUID.randomUUID().toString();
String nanshan = UUID.randomUUID().toString();
String guangxi = UUID.randomUUID().toString();
list.add(new TreeMember2("广东", null, guangdong));
list.add(new TreeMember2("深圳", guangdong, shenzhen));
list.add(new TreeMember2("南山", shenzhen, nanshan));
list.add(new TreeMember2("广西", null, guangxi));
List<TreeMember2> tree = list2Tree(list);
System.out.println("tree = " + tree);
}