获取树深度

概要:
使用BFS(广度优先搜索)、DFS(深度优先搜索)的递归和非递归方式获取树深度。

代码:
Util类:

package com.example.study.util;

import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.stream.Collectors;

/**
 * 获取树深度
 * 注:若最深一层只有null元素,该层不计入深度
 */
public class TreeDepthUtil {
    private static final Byte FLAG = 1;

    /**
     * dfs的递归实现获取树深度
     *
     * @param root       根节点
     * @param childField 子节点列表字段名称
     * @param <T>        节点类型
     * @return 树深度
     */
    public static <T> int getTreeDepthByDfsWithRecursion(T root, String childField) {
        if (root == null || !StringUtils.hasText(childField)) {
            return 0;
        }
        List<Integer> depth = new ArrayList<>(1);
        depth.add(0);
        // 这里只是需要一个标志,表示当前路径上的点,所以用byte减少内存占用
        Stack<Byte> stack = new Stack<>();
        doGetTreeDepthByDfs(root, childField, stack, depth);
        return depth.get(0);
    }

    private static <T> void doGetTreeDepthByDfs(T parent, String childField, Stack<Byte> stack, List<Integer> depth) {
        stack.push(FLAG);
        depth.set(0, Math.max(depth.get(0), stack.size()));
        Collection<T> childs = (Collection<T>) getFieldValue(parent, childField);
        if (CollectionUtils.isEmpty(childs)) {
            stack.pop();
            return;
        }
        for (T child : childs) {
            if (child == null) {
                continue;
            }
            doGetTreeDepthByDfs(child, childField, stack, depth);
        }
        stack.pop();
    }

    /**
     * dfs的非递归(循环)实现获取树深度
     *
     * @param root       根节点
     * @param childField 子节点列表字段名称
     * @param <T>        节点类型
     * @return 树深度
     */
    public static <T> int getTreeDepthByDfsWithLoop(T root, String childField) {
        if (root == null || !StringUtils.hasText(childField)) {
            return 0;
        }
        Stack<Iterator> stack = new Stack<>();
        List<T> list = new ArrayList<>();
        list.add(root);
        Iterator<T> iterator = list.iterator();
        Iterator<T> emptyIterator = (Iterator<T>) Collections.emptyList().iterator();
        int depth = 0;
        while (true) {
            while (!iterator.hasNext() && !stack.isEmpty()) {
                iterator = stack.pop();
            }
            if (!iterator.hasNext() && stack.isEmpty()) {
                break;
            }
            stack.push(iterator);
            depth = Math.max(depth, stack.size());
            T next = iterator.next();
            Collection<T> childs = (Collection<T>) getFieldValue(next, childField);
            if (childs != null) {
                childs = childs.stream().filter(o -> o != null).collect(Collectors.toList());
            }
            iterator = childs == null ? emptyIterator : childs.iterator();
        }
        return depth;
    }

    /**
     * bfs的递归实现获取树深度
     *
     * @param root       根节点
     * @param childField 子节点列表字段名称
     * @param <T>        节点类型
     * @return 树深度
     */
    public static <T> int getTreeDepthByBfsWithRecursion(T root, String childField) {
        if (root == null || !StringUtils.hasText(childField)) {
            return 0;
        }
        List<T> parents = new ArrayList<>();
        parents.add(root);
        List<Integer> depth = new ArrayList<>(1);
        depth.add(0);
        doGetTreeDepthByBfs(parents, childField, depth);
        return depth.get(0);
    }

    private static <T> void doGetTreeDepthByBfs(Collection<T> parents, String childField, List<Integer> depth) {
        if (parents.isEmpty()) {
            return;
        }
        depth.set(0, depth.get(0) + 1);
        readChilds(parents, childField);
        doGetTreeDepthByBfs(parents, childField, depth);
    }

    /**
     * bfs的非递归(循环)实现获取树深度
     *
     * @param root       根节点
     * @param childField 子节点列表字段名称
     * @param <T>        节点类型
     * @return 树深度
     */
    public static <T> int getTreeDepthByBfsWithLoop(T root, String childField) {
        if (root == null || !StringUtils.hasText(childField)) {
            return 0;
        }
        List<T> parents = new ArrayList<>();
        parents.add(root);
        int depth = 0;
        while (!parents.isEmpty()) {
            depth++;
            readChilds(parents, childField);
        }
        return depth;
    }


    private static <T> void readChilds(Collection<T> parents, String childField) {
        List<T> allChilds = new ArrayList<>();
        for (T parent : parents) {
            Collection<T> childs = (Collection<T>) getFieldValue(parent, childField);
            if (childs == null) {
                continue;
            }
            allChilds.addAll(childs.stream().filter(o -> o != null).collect(Collectors.toList()));
        }
        parents.clear();
        parents.addAll(allChilds);
    }

    /**
     * 获取字段值
     *
     * @param element   待获取值的对象
     * @param fieldName 字段名称
     * @param <T>       对象类型
     * @return 字段值
     */
    private static <T> Object getFieldValue(T element, String fieldName) {
        try {
            Field field = element.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(element);
        } catch (IllegalAccessException exception) {
            throw new RuntimeException("no permission to read field [" + fieldName + "]!");
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("field [" + fieldName + "] not exists!");
        }
    }
}

测试;
部门信息类:

package com.example.study.entity;

import lombok.Data;

import java.util.List;

/**
 * 部门实体类
 */
@Data
public class DepartmentEntity {
    /**
     * 部门id
     */
    private Integer id;
    /**
     * 部门名称
     */
    private String name;
    /**
     * 上级部门id
     */
    private Integer parentId;

    /**
     * 子部门列表
     */
    private List<DepartmentEntity> childs;
}

测试类:

package com.example.study.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.example.study.entity.DepartmentEntity;

import java.util.List;

public class Test {
    public static void main(String[] args) {
        String data = "[ { \"id\": 0, \"name\": \"部门0(根部门)\", \"parentId\": -1 }, { \"id\": 1, \"name\": \"部门1\", \"parentId\": 0 }, { \"id\": 2, \"name\": \"部门2\", \"parentId\": 0 }, { \"id\": 3, \"name\": \"部门3\", \"parentId\": 0 }, { \"id\": 4, \"name\": \"部门4\", \"parentId\": 2 }, { \"id\": 5, \"name\": \"部门5\", \"parentId\": 2 }, { \"id\": 6, \"name\": \"部门6\", \"parentId\": 3 }, { \"id\": 7, \"name\": \"部门7\", \"parentId\": 3 }, { \"id\": 8, \"name\": \"部门8\", \"parentId\": 6 }, { \"id\": 9, \"name\": \"部门9\", \"parentId\": 7 }, { \"id\": 10, \"name\": \"部门10\", \"parentId\": 9 }, { \"id\": 11, \"name\": \"部门11\", \"parentId\": 9 } ]";
        List<DepartmentEntity> departments = JSONArray.parseArray(data, DepartmentEntity.class);
        DepartmentEntity root = departments.stream().filter(o -> o.getParentId() == -1).findFirst().get();
        ListTreeConvertUtil.listToTree(departments, root, "parentId", "id", "childs");
        System.out.println(JSON.toJSONString(root));

        DepartmentEntity tmp = new DepartmentEntity();
        root.getChilds().add(1, null);
        root.getChilds().get(3).getChilds().get(0).getChilds().get(0).setChilds(null);
        root.getChilds().get(3).getChilds().get(1).getChilds().get(0).getChilds().get(1).getChilds().add(null);
        root.getChilds().get(3).getChilds().get(1).getChilds().get(0).getChilds().get(1).getChilds().add(tmp);

        System.out.println(JSON.toJSONString(root));
        System.out.println(TreeDepthUtil.getTreeDepthByDfsWithRecursion(root, "childs"));
        System.out.println(TreeDepthUtil.getTreeDepthByDfsWithLoop(root, "childs"));
        System.out.println(TreeDepthUtil.getTreeDepthByBfsWithRecursion(root, "childs"));
        System.out.println(TreeDepthUtil.getTreeDepthByBfsWithLoop(root, "childs"));

    }
}

测试结果:

{"childs":[{"childs":[],"id":1,"name":"部门1","parentId":0},{"childs":[{"childs":[],"id":4,"name":"部门4","parentId":2},{"childs":[],"id":5,"name":"部门5","parentId":2}],"id":2,"name":"部门2","parentId":0},{"childs":[{"childs":[{"childs":[],"id":8,"name":"部门8","parentId":6}],"id":6,"name":"部门6","parentId":3},{"childs":[{"childs":[{"childs":[],"id":10,"name":"部门10","parentId":9},{"childs":[],"id":11,"name":"部门11","parentId":9}],"id":9,"name":"部门9","parentId":7}],"id":7,"name":"部门7","parentId":3}],"id":3,"name":"部门3","parentId":0}],"id":0,"name":"部门0(根部门)","parentId":-1}
{"childs":[{"childs":[],"id":1,"name":"部门1","parentId":0},null,{"childs":[{"childs":[],"id":4,"name":"部门4","parentId":2},{"childs":[],"id":5,"name":"部门5","parentId":2}],"id":2,"name":"部门2","parentId":0},{"childs":[{"childs":[{"id":8,"name":"部门8","parentId":6}],"id":6,"name":"部门6","parentId":3},{"childs":[{"childs":[{"childs":[],"id":10,"name":"部门10","parentId":9},{"childs":[null,{}],"id":11,"name":"部门11","parentId":9}],"id":9,"name":"部门9","parentId":7}],"id":7,"name":"部门7","parentId":3}],"id":3,"name":"部门3","parentId":0}],"id":0,"name":"部门0(根部门)","parentId":-1}

6
6
6
6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值