首页 > 其他分享 >构建树结构(节点级别,全路径)

构建树结构(节点级别,全路径)

时间:2023-11-23 16:37:32浏览次数:37  
标签:node return iterator 树结构 路径 children public 节点

package org.example.tree;

import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @ClassName TreeUtils2
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:39
 */
public class TreeUtils<N extends TreeNode<T, RC, LC>, T, RC, LC> {

    // 所有结点
    private List<N> nodeList;
    // 所有根节点
    private List<N> rootList;

    public TreeUtils(List<N> nodeList) {
        this.nodeList = new ArrayList<>(nodeList);
        this.rootList = new ArrayList<>();
    }

    public List<N> getTree() {
        return this.rootList;
    }

    /**
     * 根据所有树节点列表,按默认条件生成含有所有树形结构的列表
     * 主要用于组建简单树形结构
     *
     * @return 树形结构列表
     */
    public TreeUtils generateTrees() {
        // 使用迭代器操作list元素
        for (Iterator<N> iterator = this.nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            if (node.isRoot()) {
                node.setLevel(0);
                node.setPath("/" + node.getId()+"/");
                // 获取所有的根节点
                rootList.add(node);
                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                iterator.remove();
            }
        }
        rootList.forEach(r -> {
            fillLeaves(r);
        });
        return this;
    }

    /**
     * 根据所有树节点列表,按自定义条件---->获取符合条件的父节点
     *
     * @return 按自定义条件获取符合条件的父节点
     */
    public TreeUtils setRoots() {
        rootList.clear();
        // 使用迭代器操作list元素
        for (Iterator<N> iterator = nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            // 按条件筛选根节点
            if (node.isRoot()) {
                node.setLevel(0);
                // 获取所有的根节点
                rootList.add(node);

                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                iterator.remove();
            }
        }
        // 返回按条件查询到的父节点
        return this;
    }

    /**
     * 根据所有树节点列表,按自定义条件---->获取符合条件的父节点
     *
     * @param rootCondition 父节点的判定规则
     * @return 按自定义条件获取符合条件的父节点
     */
    public TreeUtils setRoots(RC rootCondition) {
        rootList.clear();
        // 使用迭代器操作list元素
        for (Iterator<N> iterator = nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            // 按条件筛选根节点
            if (node.isRoot(rootCondition)) {
                node.setLevel(0);
                // 获取所有的根节点
                rootList.add(node);

                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                iterator.remove();
            }
        }
        // 返回按条件查询到的父节点
        return this;
    }

    public TreeUtils setChildren(LC leafCondition) {
        if (CollectionUtils.isEmpty(rootList)) {
            return this;
        } else {
            rootList.forEach(r -> {
                fillLeaves(r, leafCondition);
            });
            return this;
        }
    }

    public TreeUtils setChildren() {
        if (CollectionUtils.isEmpty(rootList)) {
            return this;
        } else {
            rootList.forEach(r -> {
                fillLeaves(r);
            });
            return this;
        }
    }

    /**
     * 给父节点填充叶子结点
     *
     * @param parent 父节点
     */
    public void fillLeaves(N parent) {
        List<N> children = new ArrayList<>();
        for (Iterator<N> ite = nodeList.iterator(); ite.hasNext(); ) {
            N node = ite.next();
            // 找出与当前父节点关联的叶子结点
            if (Objects.equals(node.getParentId(), parent.getTreeNodeId())) {
                node.setLevel(parent.getLevel() + 1);
                node.setPath(parent.getPath() + node.getId()+"/");
                children.add(node);
                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                ite.remove();
            }
        }
        // 继续递归叶子结点的遍历子节点
        children.forEach(m -> {
            // 递归设置子节点
            fillLeaves(m);
        });
        // 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
        if (children.isEmpty()) {
            return;
        }
        parent.setChildren(children);
    }

    /**
     * 按照自定义的规则,给父节点填充叶子结点
     *
     * @param parent        父节点
     * @param leafConfition 叶子结点的自定义判定规则的参数类型
     */
    public void fillLeaves(N parent, LC leafConfition) {
        List<N> children = new ArrayList<>();
        Object parentId = parent.getTreeNodeId();
        for (Iterator<N> ite = nodeList.iterator(); ite.hasNext(); ) {
            N node = ite.next();
            // 按自定义条件筛选子节点,null则表示没有自定义条件
            if (Objects.isNull(leafConfition) || node.isChildren(leafConfition)) {
                // 找出与当前父节点关联的叶子结点
                if (Objects.equals(node.getParentId(), parentId)) {
                    node.setLevel(parent.getLevel() + 1);
                    children.add(node);
                    // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                    ite.remove();
                }
            }
        }
        // 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
        if (children.isEmpty()) {
            return;
        }
        // 继续递归叶子结点的遍历子节点
        children.forEach(m -> {
            // 递归设置子节点
            fillLeaves(m, leafConfition);
        });
        parent.setChildren(children);
    }

    /**
     * 根据结点id获取特定结点的子树,通过isParent来判断是否需要父节点
     *
     * @param rootId 结点id
     * @param <T>    结点id的对象类型
     * @return
     */
    public <T> TreeUtils getTreeByNodeId(T rootId) {
        for (Iterator<N> iterator = nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            if (rootId.equals(node.getTreeNodeId())) {
                node.setLevel(0);
                rootList.add(node);
                iterator.remove();
            }
        }
        // 填充叶子结点
        rootList.forEach(root -> fillLeaves(root));
        return this;
    }


    /**
     * 遍历树型结构,并且根据回调函数执行相应的操作处理
     *
     * @param handle 回调函数
     * @param <K>    结果集的key
     * @param <V>    结果集的value
     * @return 返回一个结果集的map
     */
    public static <N extends TreeNode, K, V> Map<K, V> traverseTree(List<N> tree, FunctionHandle<N, K, V> handle) {
        Map<K, V> resultMap = new HashMap<>();
        for (Iterator<N> iterator = tree.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            // 通过回调函数处理当前点
            if (handle != null) {
                handle.callback(node, resultMap);
            }
            if (node.hasChild()) {
                recursiveTree(node.getChildren(), resultMap, handle);
            }
        }
        return resultMap;
    }


    /**
     * 递归遍历子树,获取相应的处理结果
     *
     * @param children  子树集合
     * @param resultMap 结果集
     * @param handle    回调函数
     * @param <E>       集合类型
     */
    private static <E extends TreeNode> void recursiveTree(List<E> children, Map resultMap, FunctionHandle handle) {
        for (Iterator<E> iterator = children.iterator(); iterator.hasNext(); ) {
            E child = iterator.next();
            if (handle != null) {
                handle.callback(child, resultMap);
            }
            if (child.hasChild()) {
                recursiveTree(child.getChildren(), resultMap, handle);
            }
        }
    }
}





package org.example.tree;

import java.util.List;

/**
 * @ClassName TreeNode
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:41
 */
public interface TreeNode<T, RC, LC> {

    /**
     * 获取树结点id
     * @return
     */
    T getTreeNodeId();

    /**
     * 获取该节点的父节点id
     * @return
     */
    T getParentId();

    /**
     * 判断该节点是否为根节点
     * @Des 可以用于简单树的组件
     * @return
     */
    boolean isRoot();

    /**
     * 自定义父结点的判定规则
     * @param rootCondition
     * @return
     */
    boolean isRoot(RC rootCondition);

    /**
     * 自定义子节点(叶子结点)的判定规则
     * @param leafCondition
     * @return
     */
    boolean isChildren(LC leafCondition);

    /**
     * 判断是否有子节点
     * @return
     */
    boolean hasChild();

    /**
     * 设置结点的子节点列表
     * @param children
     */
    void setChildren(List<? extends TreeNode<T, RC, LC>> children);

    /**
     * 获取所有子节点
     * @return
     */
    List<? extends TreeNode<T, RC, LC>> getChildren();

    /**
     * 获取树的深度
     * @return
     */
    Integer getLevel();

    /**
     * 设置树的深度
     */
    void setLevel(Integer level);

    /**
     * 设置树的路径
     */
    Integer getId();

    /**
     * 设置树的深度
     */
    void setId(Integer id);

    /**
     * 设置树的路径
     */
    String getPath();

    /**
     * 设置树的深度
     */
    void setPath(String path);

}



package org.example.tree;

import java.util.Map;

/**
 * @ClassName FunctionHandle
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:42
 */
@FunctionalInterface
public interface FunctionHandle <N, K, V> {
    void callback(N node, Map<K,V> result);
}




package org.example.tree;

import lombok.Data;

import java.util.List;
import java.util.Objects;

/**
 * @ClassName MenuVo
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:43
 */
@Data
public class MenuVo implements TreeNode<Integer, Boolean, Integer> {

    private Integer id;

    private String name;

    private Integer parentId;

    private Integer Level;

    private String path;

    private List<MenuVo> children;



    public MenuVo(Integer id, String name, Integer parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
    }


    @Override
    public Integer getTreeNodeId() {
        return this.id;
    }

    @Override
    public Integer getParentId() {
        return this.parentId;
    }

    @Override
    public boolean isRoot() {
        return Objects.equals(this.parentId, 0);
    }


    @Override
    public boolean isRoot(Boolean rootCondition) {
        if (rootCondition){
//            Objects.nonNull();
            return Objects.equals(this.id, 14);

        } else {
            // 都不符合就走默认判定条件
            return isRoot();
        }
    }

    @Override
    public boolean isChildren(Integer leafCondition) {
        // 这里自定义规则当传入的参数等于1的时候 ——> 要销售部,只要销售部的A部门的子树节点
        if (leafCondition.equals(1)){
            if (this.name.contains("a")){
                return true;
            } else {
                return false;
            }
        } else {
            // 都不符合就表示该节点不是自定义规则中要的结点
            return false;
        }

    }

    @Override
    public boolean hasChild() {
        return !Objects.isNull(this.children);
    }

    @Override
    public void setChildren(List children) {
        this.children = children;
    }

    @Override
    public List<MenuVo> getChildren(){
        return this.children;
    }



}




@Test
public void testtree2(){
    //模拟从数据库查询出来
    List<MenuVo> menusVoList = new ArrayList<>(Arrays.asList(
            new MenuVo(1, "A公司", 0),
            new MenuVo(2, "a销售部", 14),
            new MenuVo(3, "财税部", 1),
            new MenuVo(4, "商务部", 1),
            new MenuVo(5, "综合部", 1),
            new MenuVo(6, "a销售1部", 2),
            new MenuVo(7, "a销售2部", 2),
            new MenuVo(8, "a销售3部", 2),
            new MenuVo(9, "a销售4部", 2),
            new MenuVo(10, "b销售部", 14),
            new MenuVo(11, "b销售1部", 10),
            new MenuVo(12, "b销售2部", 10),
            new MenuVo(13, "人事部", 1),
            new MenuVo(14, "销售部", 1)));

    List simpleTree = new TreeUtils<>(menusVoList).generateTrees().getTree();
    System.out.println(JSON.toJSON(simpleTree));

}

 

标签:node,return,iterator,树结构,路径,children,public,节点
From: https://www.cnblogs.com/message-hrp/p/17851855.html

相关文章

  • python通过脚本路径获取对应脚本里的内容
    test.pyclassA:defa(self):pass@staticmethoddefb():pass@classmethoddefc(cls):pass@propertydefd(self):return1e=1deff():passtest2.pyimportinspectimportosfromimp......
  • MySql存储树形结构,Java实现根据节点找到父节点,根据节点找到子节点
    目录数据表设计生成树(递归方式)根据节点cId返回所有的父节点pId数据表设计idparent_idnamelevel10食物121蔬菜231水果242茄果类352叶菜类363浆果类373瓜果类384番茄494辣椒4105生菜4116桑葚4id......
  • Exadata存储节点大量nvmecli进程,导致系统出现卡顿现象
    1、故障概要同事在执行Exadata巡检时,发现客户Exadata环境中的celadm01存储节点存在卡顿的现象。相同的命令,在其他的存储节点很快就返回输出结果,而celadm01这台存储节点需要很长时间才返回输出结果。 2、故障分析(1).检查主机负载情况。发现celadm01这台存储节点的负载(loadave......
  • 图 - 拓扑排序 & 关键路径
    图-拓扑排序&关键路径拓扑排序AOV网DAG图:有向无环图AOV(ActivitiesOnVertexNetwork)网:用顶点表示活动,用弧表示活动间的优先关系的网.AOV网中不会出现自环(有向环),这意味着有的活动以他自己为前提。拓扑排序按照优先顺序对AOV网中的顶点进行排序使之形成一个线性序列。......
  • Linux内核中NUMA内存节点和内存zone
      在现代大型服务器中多个内存节点机器一般都采用NUMA架构,而NUMA架构中不同的内存节点在Linux内核中使用pg_data_t类型(实际是structpglist_data)来表示表示。   Linux又为每个内存节点根据内存地址的高低划分了不同的区域类型如ZONE_DMA、ZONE_DMA32、ZONE_NORMAL,一个......
  • 集成 NVDC 电源路径管理的1-4节电池升降压充电IC解决方案
    描述MP2760是一款集成窄电压DC(NVDC)电源路径管理功能和USBOn-the-Go(OTG)功能的升降压充电IC,兼容USBPD,适用于单节至4节串联的电池包应用。该芯片的充电输入电压范围广,可支持最高22V。当启用电池放电模式(Sourcemode)时,芯片的IN引脚可提供高达21V的电压。当提供电源输入时,MP2760通......
  • (链表)17-两两交换链表中的节点
    1/**2*Definitionforsingly-linkedlist.3*publicclassListNode{4*intval;5*ListNodenext;6*ListNode(){}7*ListNode(intval){this.val=val;}8*ListNode(intval,ListNodenext){this.val=val;......
  • (链表)09-删除链表的倒数第N个节点
    1importjava.util.*;23/*4*publicclassListNode{5*intval;6*ListNodenext=null;7*}8*/9publicclassSolution{10/**11*@paramheadListNode类12*@paramnint整型13*@returnListNode类14......
  • 两两交换链表中的节点
    现在时间是:  2023年11月18日 星期六   农历十月初六  22:08:每天坚持刷Leetcode,遇到有些突然看到就不能想得很清楚的题目,还是需要进行记录一下!Leetcode热题100(学习计划):两两交换链表中的节点,题目信息如下:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节......
  • python 根据绝对路径关闭进程
    importosimportpsutil#如果未知路径且写入了配置环境#os.system("taskkill/f/imexcel.exe&taskkill/f/imwps.exe")#cmdtaskkill直接输入不需加双引号#cmdtaskkill无法根据绝对路径关闭程序无论有没有双引号(无效查询或没有找到进程)#True,False,N......