Java 树结构的构造及树中数据自下向上的汇总(递归和非递归实现)

本文介绍如何使用递归和非递归方法构建树结构并进行数据汇总,包括机构类的设计、递归构建树的过程及注意事项,以及两种数据汇总的方法。

关于树结构的遍历,可以参考:
java-树的遍历

在项目中,经常会遇到需要对机构的一些数据进行统计汇总的情况,而这些机构又存在着一些子机构,这时可以考虑使用树结构来解决问题.

注意: 本文使用的情景是已经对机构进行了层级排序,并且同时只存在一个根机构.如果不符合你的需求,你需要根据自己的业务进行修改.

机构

public class Org<T> {

    private Integer orgId;
    private Integer parentId;
    private String orgName;
    private Integer level;
    private T data;
    private List<Org<T>> children;
 }

机构类使用了泛型,这样方便与各种不同的业务数据进行组合使用,这里添加了level属性,用来区分机构的层级,方便对机构进行排序.如果你没有这个属性的话,也没什么大的问题,可能你需要使用其他方式来进行排序了.

递归实现树的构造

代码如下:

 	//递归 建立树形结构
    public static <T> Org<T> buildTreeWithRecursion(List<Org<T>> orgList) {
        long l = System.nanoTime();
        //1.
        if (orgList == null || orgList.size() == 0) {
            return null;
        }
        if (orgList.size() == 1) {
            return orgList.get(0);
        }
		
		//2.
        Org<T> rootOrg = null;
        for (Org<T> org : orgList) {
            if (org.getLevel() == 0) {
                rootOrg = org;
                break;
            }
        }
        //3.
        if (rootOrg != null) {
            buildChildTree(rootOrg, orgList);
        }
        System.out.println("buildTreeWithRecursion ,cost " + (System.nanoTime() - l) + " ns");
        return rootOrg;
    }

	//递归,建立子树形结构
    private static <T> Org<T> buildChildTree(Org<T> pNode, List<Org<T>> orgList) {
        List<Org<T>> children = pNode.getChildren();
        if (children == null) {
            children = new ArrayList<Org<T>>();
            pNode.setChildren(children);
        }

        for (Org<T> org : orgList) {
            if (org.getParentId().equals(pNode.getOrgId())) {
                if (!children.contains(org)) {
                    children.add(buildChildTree(org, orgList));
                }
            }
        }
        return pNode;
    }
  1. 对传递进来的机构列表进行了一些常规判断.
  2. 查找机构的根机构,由于我们的前提是只有一个根机构,且已经对机构进行了层级的排序,此处直接取列表的第一个元素也可以.如果没有层级数据的话,可能需要进行遍历,查找父机构id为空的机构作为根机构.
  3. 调用递归方法,构造树结构.

非递归实现树的构造

先上代码:

 public static <T> Org<T> buildTreeWithMap(List<Org<T>> orgList, Map<Integer, Org<T>> orgMap) {
        long l = System.nanoTime();
        //1.
        if (orgList == null || orgList.size() == 0) {
            return null;
        }
        if (orgList.size() == 1) {
            return orgList.get(0);
        }
		
		//2.
        if (orgMap == null) {
            orgMap = new HashMap<Integer, Org<T>>();
            for (Org<T> org : orgList) {
                orgMap.put(org.getOrgId(), org);
            }
        }
		
		//3.
        for (Org<T> org : orgList) {
            Integer parentId = org.getParentId();
            Org<T> parentOrg = orgMap.get(parentId);
            if (parentOrg != null) {
                List<Org<T>> children = parentOrg.getChildren();
                if (children == null) {
                    children = new ArrayList<Org<T>>();
                    parentOrg.setChildren(children);
                }
                if (!children.contains(org)) {
                    children.add(org);
                }
            }
        }
        System.out.println("buildTreeWithMap cost: " + (System.nanoTime() - l) + " ns");
        return orgList.get(0);
    }
  1. 对机构列表进行一些常规判断.
  2. 此处的orgMap是用来进行构造的辅助Map,因为我的业务中已经存在该数据了,所有我直接传递进来了,如果不存在的话,可以在方法内部新建即可. 该Map将机构列表中的机构以机构id为key,机构为value进行了存储,方便下一步我们根据机构id来获取对应机构.
  3. 遍历机构列表,根据当前机构的父机构的id来找到对应的父级机构,把自己加入到父级机构的子机构列表中.

注意:此处不太直观,需要理解一下Java中的 对象和引用 的区别,我们在此进行的各种操作都是对列表中的实际机构对象的引用的操作

我们把机构C加入到父机构P内部的子机构列表的时候,其实只是把C对象的引用加入了进去. 因此我们再把别的机构D的引用加入到该机构C的子机构列表的时候,在其父机构P中的子机构列表中的C机构的引用由于同样指向C,因此此时P机构的三级子节点得到了完善. 在遍历完成的时候,整个树机构就已经完成了.查看列表后就可以发现,列表的第一个元素就是完整的树结构,列表的其他元素则是以该元素为根机构形成的一个子的树机构,是完整的树结构的一部分.

递归实现数据的汇总

汇总数据是在构造了树之后,对构造之后的机构列表进行汇总,即我们先把列表作为参数进行了树的构造,然后再把列表作为参数来进行数据汇总.

注意: 使用递归进行汇总时,使用完整的树结构即可,即构造后的机构列表的第一个元素

   public static Org<Long> calculateWithRecursion(List<Org<Long>> orgList) {
        long l = System.nanoTime();
        if (orgList == null || orgList.size() == 0) {
            return null;
        }

        Org<Long> rootOrg = orgList.get(0);

        calculateChildren(rootOrg);
        System.out.println("calculateWithRecursion cast: " + (System.nanoTime() - l) + " ns");
        return rootOrg;
    }

    private static Org<Long> calculateChildren(Org<Long> pNode) {
        if (pNode == null || pNode.getChildren() == null
                || pNode.getChildren().size() == 0) {
            return pNode;
        }
        Long c = pNode.getData();
        for (Org<Long> child : pNode.getChildren()) {
            Long count = calculateChildren(child).getData();
            c += count;
        }
        pNode.setData(c);
        return pNode;
    }

此处是简单的把一个Long作为数据对象与Org组合在一起,实际业务中可以使用更复杂的对象来进行处理.由于此处我把一些实际的业务处理代码进行了删除,可能有一些问题,自行理解整体思路即可.

非递归实现数据汇总

汇总数据是在构造了树之后,对构造之后的机构列表进行汇总,即我们先把列表作为参数进行了树的构造,然后再把列表作为参数来进行数据汇总.

注意:此处使用了完整的机构列表,而递归实现中只使用了列表的第一个元素

非递归的话,我们从列表的尾部开始遍历进行汇总,这样的实现原理跟非递归进行树构造的原理是一样的,如果一时不理解的话可以多思考思考,实际操作一下.

	public static Org<Long> calculateWithoutRecursion(List<Org<Long>> orgList) {
        long l = System.nanoTime();
        if (orgList == null || orgList.size() == 0) {
            return null;
        }

        Org<Long> rootOrg = orgList.get(0);

        //从节点列表的末尾开始向上汇总
        int size = orgList.size();
        for (int i = size - 1; i >= 0; i--) {
            Org<Long> org = orgList.get(i);
            List<Org<Long>> children = org.getChildren();
            if (children != null && children.size() > 0) {
                Long data = org.getData();
                for (Org<Long> child : children) {
                    data += child.getData();
                }
                org.setData(data);
            }
        }
        System.out.println("calculateWithoutRecursion cost: " + (System.nanoTime() - l) + " ns");
        return rootOrg;
    }

在这里记录一下思路,大家可以根据自己的需要进行进一步的优化.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值