JAVA后端生成树算法,从指定的叶子节点到树根生成树,从树根到所有叶子结点

4/11 10:20 更新了一些算法解释

4/18 17:17 更新了生成子树的方法,更加灵活了

先贴代码吧


import java.util.*;

/**
 * @Author xjd
 * @Date 2019/4/3 10:30
 * @Version 1.0
 */
public class GeTreeUtil {

    /**
     * 传入的节点结构必须继承本工具支持的数据结构
     * @author xjd
     * @date 2019/4/10 17:20
     */
    public static class BaseTreeNode{
        protected Long id;
        protected Long pid;
        protected Boolean leafNode;
        protected List<BaseTreeNode> childList;

        protected BaseTreeNode(Long id, Long pid, Boolean leafNode, List<BaseTreeNode> childList) {
            this.id = id;
            this.pid = pid;
            this.leafNode = leafNode;
            this.childList = childList;
        }
    }

    /**
     * 根据叶子节点生成只包含叶子节点分支的树,可以是深林,最终汇成一棵树
     * @param allNodeList 整棵树或者整个深林的所有的节点
     * @param leafNodeList 需要展示的叶子节点
     * @return 分支树
     */
    public static List<? extends BaseTreeNode> getBranchTree(List<? extends BaseTreeNode> allNodeList, List<? extends BaseTreeNode> leafNodeList){
        Hashtable<Long, List<BaseTreeNode>> dicNode = new Hashtable<>();
        for (BaseTreeNode baseTreeNode : leafNodeList) {
            List<BaseTreeNode> childList = dicNode.get(baseTreeNode.pid);
            if(null == childList){
                childList = new ArrayList<>();
                dicNode.put(baseTreeNode.pid, childList);
            }
            childList.add(baseTreeNode);
        }
/*        Map<Long, BaseTreeNode> allNodeMap = new HashMap<>();
        for (BaseTreeNode node : allNodeList) {
            allNodeMap.put(node.id, node);
        }*/

        if(dicNode.isEmpty()){
            return Collections.emptyList();
        }
        while (!(dicNode.containsKey(-1L) && dicNode.size() == 1)){ // 递归出口
            ConbineDirectParant(allNodeList, dicNode);
//            dicNode = ConbineDirectParant(allNodeMap, dicNode); // 这种实现慢一些
        }
        return dicNode.get(-1L);
    }

    /**
     * 自底向上,每次每个叶子节点上升一层,查找并合并一层的父子关系,通过dicNode里面的父子关系把children并到父节点数据结构的childList中
     * @param allNodeList 所有节点,已经找到祖宗孩子关系的节点就remove,不重复添加
     * @param dicNode 每次只存一层关系合并完就删除,最后只剩下根节点和第一层节点的父子关系
     */
    public static void ConbineDirectParant(List<? extends BaseTreeNode> allNodeList, Hashtable<Long, List<BaseTreeNode>> dicNode){
        Iterator<BaseTreeNode> iterator = (Iterator<BaseTreeNode>) allNodeList.iterator();
        while (iterator.hasNext()){
            BaseTreeNode parentNode = iterator.next();
            if(dicNode.containsKey(parentNode.id)){ // 爸爸
                List<BaseTreeNode> childList = dicNode.get(parentNode.id);
                // 把父子关系保存在node结构体,dicNode的父子关系就可以remove了
                parentNode.childList.addAll(childList);
                AddOneNode(parentNode, dicNode);
                //  删除已经用完了的下一级,构造dicNode.size==1的递归出口,保证不同树叶子节点都可以递归到深林的根
                dicNode.remove(parentNode.id);
                iterator.remove();
            }
        }
    }

    /**
     * 更新dicNode的父子关系,把当前节点合并到上一层的父子关系或者升级到上一层的父子关系
     * @param node dicNode中的Key对应的节点,也就是value的父节点
     * @param dicNode
     */
    private static void AddOneNode(BaseTreeNode node, Hashtable<Long, List<BaseTreeNode>> dicNode) {
        List<BaseTreeNode> childList = dicNode.get(node.pid);
        if(childList != null){
            childList.add(node);
        }else {
            dicNode.put(node.pid, new ArrayList<>(Arrays.asList(node)));
        }
    }

    /**
     * 传入一棵树或者深林的需要展示的所有节点,生成深林或者树机构
     * @param valueList 需要展示的所有节点
     * @return 一棵树或一个深林
     */
    public static List<?> geFullTree(List<? extends BaseTreeNode> valueList){

        Map<Long, BaseTreeNode> nodeMap = new HashMap<>();
        List<BaseTreeNode> rootNodeList = new ArrayList<>();

        for (BaseTreeNode node : valueList) {
            Long id = node.id;
            Long pid = node.pid;
            BaseTreeNode baseTreeNode = nodeMap.get(id);
            if (baseTreeNode != null) {
                node.childList.addAll(baseTreeNode.childList);
//                nodeMap.remove(id); // 会自动覆盖相同key,不用remove
            }
            nodeMap.put(id, node);

            BaseTreeNode parentNode = nodeMap.get(pid);
            if (parentNode != null) {
                if(parentNode.childList == null){
                    parentNode.childList = new ArrayList<>();

                }
                parentNode.childList.add(node);
            }else{
                nodeMap.put(pid, new BaseTreeNode(pid, null, null, new ArrayList<>(Arrays.asList(node))));
            }

            if(new Long(-1L).equals(pid)){
                rootNodeList.add(node);
            }
        }

        return rootNodeList;
    }

    /**
     * 传入所有结点,和需要生成的子树的id
     * @param valueList
     * @param childTreeRootId
     * @return
     */
    public static BaseTreeNode GeChildTree(List<? extends BaseTreeNode> valueList, Long childTreeRootId){

        Map<Long, BaseTreeNode> nodeMap = new HashMap<>();

        for (BaseTreeNode node : valueList) {
            Long id = node.id;
            Long pid = node.pid;
            BaseTreeNode childNode = nodeMap.get(id);
            if (childNode == null) {
                childNode = node;
            }else{
                // 遍历到孩子的时候已经put,只拿childlist复制到node
                if (childNode.childList != null) {
                    if (node.childList == null) {
                        node.childList = new ArrayList<>();
                    }
                    node.childList.addAll(childNode.childList);
                }
                childNode = node;
            }
            // 每次都put为了nodeMap存的是传进来子类类型
            nodeMap.put(id, childNode);

            BaseTreeNode parentNode = nodeMap.get(pid);
            if (parentNode == null) {
                parentNode = new BaseTreeNode(pid, null, false, new ArrayList<>(Arrays.asList(childNode)));
                nodeMap.put(pid, parentNode);
            }else{
                if (parentNode.childList == null) {
                    parentNode.childList = new ArrayList<>();
                }
                parentNode.childList.add(childNode);
            }
        }
        return nodeMap.get(childTreeRootId);
    }

    /**
     * 生成分支树的另一种递归实现,直接从dicNode(一代父子关系)查找key对应的节点 而不遍历所有节点
     * @param allNodeMap
     * @param dicNode
     * @return
     */
    public static Hashtable<Long, List<TagNode>> ConbineDirectParant(Map<Long, TagNode> allNodeMap, Hashtable<Long, List<TagNode>> dicNode){
        Hashtable<Long, List<TagNode>> newdicNode = new Hashtable<>();
        for (Long aLong : dicNode.keySet()) {
            List<TagNode> childList = dicNode.get(aLong);
            TagNode parentNode = allNodeMap.get(aLong);
            if (parentNode != null){
                if(parentNode.childList == null){
                    parentNode.childList = new ArrayList<>();
                }
                for (TagNode childTagNode : childList) {
                    Integer leafNode = childTagNode.leafNode;
                    Boolean bLeafNode = new Integer(1).equals(leafNode) ? true : false;
                    BaseTreeNode baseTreeNode;
                    if(bLeafNode){
                        baseTreeNode = new TagNode(childTagNode.tagId, childTagNode.tagPid, bLeafNode, null);
                    }else{
                        baseTreeNode = new TagNode(childTagNode.tagId, childTagNode.tagPid, bLeafNode, childTagNode.childList);
                    }
                    ((TagNode) baseTreeNode).tagName = childTagNode.tagName;
                    ((TagNode) baseTreeNode).tagType = childTagNode.tagType;
                    ((TagNode) baseTreeNode).tagTypeName = childTagNode.tagTypeName;
                    parentNode.childList.add(baseTreeNode);
                }
                List<TagNode> tagNodes = dicNode.get(parentNode.tagPid);
                if (tagNodes == null) {
                    tagNodes = new ArrayList<>();
                }
                tagNodes.add(parentNode);
                newdicNode.put(parentNode.tagPid, tagNodes);

                allNodeMap.remove(aLong);
            }
        }
        return newdicNode;
    }

}

最后一个方法可以不用看,没有完善的,直接传了子类

 

只要继承我定义的BaseTreeNode结构的节点可以用这个工具

根节点为id为-1

继承BaseTreeNode需要先初始化父类的属性,子类的节点自行构造,但都必须还有父类的属性

生成整棵树的算法应该没什么亮点,把所有节点遍历一次,边存父子关系边合并到根节点。

 

难点是从叶子节点自底向上生成树

一层一层递归,好像解释都写在代码注释上了。

算法解释:

dicNode是用来存储一代的关系的key--父亲id,value--childlist

    先存一份所有叶子结点和其父节点的关系到一个HashTable里(dicNode),这个HashTable只存一代的父子关系,

每次递归 向上查一层父亲,

第一次递归能保存到叶子结点本身(存储在父节点的childList),

叶子结点的pid对应的父节点(存储在dicNode的value中,合并到已经存了伯父的list中或者创建一个暂时只有自身一个元素的list中),

父节点的pid(存储在dicNode的key中),

一共三代关系。

然后remove掉已经归并了的(已经找到组织的)key,为了最后的递归出口是所有关系都归并到一个根节点(id==-1)并且只剩下一代(最顶层的一代)关系,避免了有些叶子结点率先找到了根节点,而有些层次较低的还没找到(如果这时候跳出递归了就会只找到了层次最小的叶子结点的分支,其他的还没来得及合并就被丢弃了),所以要保证dicNode最终有且只有一个根节点关系

第二次以及后面的递归都是向上找一层,合并,最后剩下一个根节点的关系。

 

有看不懂的评论,我会看。

之前找从叶子节点生成树的方法没找到合适的,就自己尝试着研究一下。

深受张艳玲老师的开源思想的影响,就分享出来了

希望有大佬可以帮我优化一下,感觉我写得还是不够完善。

 

-----------

引用参考文章 :

如何从树叶层开始建树?

https://blog.csdn.net/xuexiaodong009/article/details/12086769

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值