树以及应用

树的遍历

树的遍历主要有3种:

1)前序遍历:根->左->右;

2)中序遍历:左->根->右;

3)后序遍历:左->右->根。

举例子:

 

前序遍历(根->左->右):A B D H E I C F J K G

中序遍历(左->根->右):D H B E I A J F K C G

后序遍历(左->右->根):H D I E B J K F G C A

前、中、后序遍历代码实现

定义TreeNode:

class TreeNode {
        private String name;
        private TreeNode left;
        private TreeNode right;

        public TreeNode(String name) {
            this.name = name;
        }

        public TreeNode(String name, TreeNode left, TreeNode right) {
            this.name = name;
            this.left = left;
            this.right = right;
        }
  
                ... // 次数省去getter/setter
}

为了确保测试结果和上边对应上,这里我先填充好和上边数结构一致的一棵树。

        TreeNode treeA = new TreeNode("A");
        TreeNode treeB = new TreeNode("B");
        TreeNode treeC = new TreeNode("C");
        TreeNode treeD = new TreeNode("D");
        TreeNode treeE = new TreeNode("E");
        TreeNode treeF = new TreeNode("F");
        TreeNode treeG = new TreeNode("G");
        TreeNode treeH = new TreeNode("H");
        TreeNode treeI = new TreeNode("I");
        TreeNode treeJ = new TreeNode("J");
        TreeNode treeK = new TreeNode("K");

        treeA.setLeft(treeB);
        treeA.setRight(treeC);

        treeB.setLeft(treeD);
        treeB.setRight(treeE);

        treeC.setLeft(treeF);
        treeC.setRight(treeG);

        treeD.setRight(treeH);

        treeE.setRight(treeI);

        treeF.setLeft(treeJ);
        treeF.setRight(treeK);

前、中、后序遍历函数定义:

    /**
    * 如果节点不为空,则打印节点name
    */
    private static void printVal(TreeNode tree) {
        if (tree != null) {
            System.out.print("->" + tree.getName());
        }
    }

    /**
     * 前序遍历:根->左->右;
     */
    private static void recursivePreOrder(TreeNode root) {
        if (root == null) return;
        printVal(root);
        recursivePreOrder(root.getLeft());
        recursivePreOrder(root.getRight());
    }

    /**
     * 中序遍历:左->根->右;
     */
    private static void recursiveInOrder(TreeNode root) {
        if (root == null) return;
        recursiveInOrder(root.getLeft());
        printVal(root);
        recursiveInOrder(root.getRight());
    }

    /**
     * 后序遍历:左->右->根。
     */
    private static void recursivePostOrder(TreeNode root) {
        if (root == null) return;
        recursivePostOrder(root.getLeft());
        recursivePostOrder(root.getRight());
        printVal(root);
    }

main函数中执行对treeA进行前、中、后序遍历,并打印遍历结果:

    public static void main(String[] args) {
        ...// 此处省去上边treeA树填充代码
          
        System.out.println("\r\n前序遍历:");
        recursivePreOrder(treeA);
      
        System.out.println("\r\n中序遍历:");
        recursiveInOrder(treeA);
      
        System.out.println("\r\n后序遍历:");
        recursivePostOrder(treeA);
    }

执行结果:

前序遍历:
->A->B->D->H->E->I->C->F->J->K->G
中序遍历:
->D->H->B->E->I->A->J->F->K->C->G
后序遍历:
->H->D->I->E->B->J->K->F->G->C->A

该结果和上边是一致的,验证了我们定义的前、中、后序遍历方法是正确的。

树的应用场景

1)前序遍历:用来实现目录结构的显示,比如:组织树;

2)中序遍历:用来做表达式树,在编译器底层实现的时候用户可以实现基本的加减乘除,比如:a*b+c;

3)后序遍历:用来实现计算机目录的文件占用的数据大小。

组织树

需求:

1)假设公司组织结构分为了以下4个目录:集团、中心、区域、分公司(实际上分公司下应该还有各自的部门等)。

2)前端当然是希望按照一定的顺序展示为一棵树(同层结构上排序自己可以定义,比如扩展TreeNode包含一个priority排序字段);

3)当中心级别人员他可管理的人员只能是中心以下级别的人员,此时我们需要根据中心id获取其子组织节点id,然后根据这些子组织节点id去查询用户;

4)有时我们又希望查看父节点有哪些组织,此时需要根据用户所属组织节点向上查找父组织节点。

分析:

即需要根据自身组织节点查找父节点,又需要满足查找它的子节点。而组织结构是一个目录结构,它是一棵树。如果我们再数节点上记录下每个节点的左节点最小权值、右节点最小权值,然后利用前序遍历分时给他们分配值。之后设计出的树实际上就成为了如下图的一棵树。

 

优缺点:

优点:

1)如果要查找A的子节点,那么需要查找min>1 and max<16的都是它的子节点,超找到的有B/C/D/E/F/G/H。

2)如果查找D节点的父节点,那么需要查钊min<3 and max>4的都是它的父节点,查找到的有A/B。

缺点:

1)如果只是简单的每增加、删除(可以忽略不做维护)节点时,维护父级节点以及相邻节点的min/max值,几乎修改了整棵树的节点min/max值。

代码实现

定义TreeNode

static class TreeNode {
    private Long id;
    private String code;
    private Long parentId;
    private String name;
    private Long lft;
    private Long rht;

    public TreeNode(Long id, String code, Long parentId, String name) {
        this.id = id;
        this.code = code;
        this.parentId = parentId;
        this.name = name;
    }
        ...// 此处省去getter/setter
}

定义异常类:

    static class NoFoundRootNodeException extends Exception {
        public NoFoundRootNodeException(String message) {
            super(message);
        }
    }

    static class EmptyTreeException extends Exception {
        public EmptyTreeException(String message) {
            super(message);
        }
    }

填充List<TreeNode> allOrgsList

        // 加载所有组织
        final List<TreeNode> allOrgsList = new ArrayList<>();
        // 集团
        allOrgsList.add(new TreeNode(1L, "1", 0L, "集团"));

        // 中心-01
        allOrgsList.add(new TreeNode(1001L, "1001L", 1L, "中心-01"));
        // 区域-》中心-01
        allOrgsList.add(new TreeNode(10010001L, "10010001L", 1001L, "中心-01-区域-01"));
        allOrgsList.add(new TreeNode(10010002L, "10010002L", 1001L, "中心-01-区域-02"));
        // 区域-》中心-01-》分公司
        allOrgsList.add(new TreeNode(1001000100001L, "1001000100001L", 10010001L, "中心-01-区域-01-分公司-01"));
        allOrgsList.add(new TreeNode(1001000100002L, "1001000100002L", 10010001L, "中心-01-区域-01-分公司-02"));
        allOrgsList.add(new TreeNode(1001000200001L, "1001000200001L", 10010002L, "中心-01-区域-02-分公司-01"));
        allOrgsList.add(new TreeNode(1001000200002L, "1001000200002L", 10010002L, "中心-01-区域-02-分公司-02"));

        // 中心-02
        allOrgsList.add(new TreeNode(1002L, "1002L", 1L, "中心-02"));
        // 区域-》中心-02
        allOrgsList.add(new TreeNode(10020001L, "10020001L", 1002L, "中心-02-区域-01"));
        allOrgsList.add(new TreeNode(10020002L, "10020002L", 1002L, "中心-02-区域-02"));
        // 区域-》中心-02-》分公司
        allOrgsList.add(new TreeNode(1002000100001L, "1002000100001L", 10020001L, "中心-02-区域-01-分公司-01"));
        allOrgsList.add(new TreeNode(1002000100002L, "1002000100002L", 10020001L, "中心-02-区域-01-分公司-02"));
        allOrgsList.add(new TreeNode(1002000200001L, "1002000200001L", 10020002L, "中心-02-区域-02-分公司-01"));
        allOrgsList.add(new TreeNode(1002000200002L, "1002000200002L", 10020002L, "中心-02-区域-02-分公司-02"));

        // 中心-03
        allOrgsList.add(new TreeNode(1003L, "1003L", 1L, "中心-03"));
        // 区域-》中心-03
        allOrgsList.add(new TreeNode(10030001L, "10030001L", 1003L, "中心-03-区域-01"));
        allOrgsList.add(new TreeNode(10030002L, "10030002L", 1003L, "中心-03-区域-02"));
        // 区域-》中心-03-》分公司
        allOrgsList.add(new TreeNode(1003000100001L, "1003000100001L", 10030001L, "中心-03-区域-01-分公司-01"));
        allOrgsList.add(new TreeNode(1003000100002L, "1003000100002L", 10030001L, "中心-03-区域-01-分公司-02"));
        allOrgsList.add(new TreeNode(1003000200001L, "1003000200001L", 10030002L, "中心-03-区域-02-分公司-01"));
        allOrgsList.add(new TreeNode(1003000200002L, "1003000200002L", 10030002L, "中心-03-区域-02-分公司-02"));
        allOrgsList.add(new TreeNode(1003000200002L, "1003000200003L", 10030002L, "中心-03-区域-02-分公司-03"));

        // 中心-04
        allOrgsList.add(new TreeNode(1004L, "1004L", 1L, "中心-04"));
        // 区域-》中心-04
        allOrgsList.add(new TreeNode(10040001L, "10040001L", 1004L, "中心-04-区域-01"));
        allOrgsList.add(new TreeNode(10040002L, "10040002L", 1004L, "中心-04-区域-02"));
        // 区域-》中心-04-》分公司
        allOrgsList.add(new TreeNode(1004000100001L, "1004000100001L", 10040001L, "中心-04-区域-01-分公司-01"));
        allOrgsList.add(new TreeNode(1004000100002L, "1004000100002L", 10040001L, "中心-04-区域-01-分公司-02"));
        allOrgsList.add(new TreeNode(1004000200001L, "1004000200001L", 10040002L, "中心-04-区域-02-分公司-01"));
        allOrgsList.add(new TreeNode(1004000200002L, "1004000200002L", 10040002L, "中心-04-区域-02-分公司-02"));

上边需要要快速实现查找父节点、子节点需要填充lft/rht,因此需要每次新增、删除(也可以处理)节点后,都需要重新修改lft/rht值。具体实现函数:

    /**
    * 重置lft/rht值
    */
    private static List<TreeNode> sortTreeNode(List<TreeNode> allOrgsList, String rootOrgCode) throws NoFoundRootNodeException, EmptyTreeException {
        // 根组织
        TreeNode rootOrg = null;
        if (allOrgsList == null || allOrgsList.isEmpty()) {
            throw new EmptyTreeException("empty org tree!");
        }

        for (TreeNode _org : allOrgsList) {
            if (rootOrgCode.equals(_org.getCode())) {
                rootOrg = _org;
                break;
            }
        }

        if (rootOrg == null) {
            throw new NoFoundRootNodeException("not found root node!");
        }

        // 更新整棵组织树的lft和rht
        List<TreeNode> treeList = new ArrayList<TreeNode>();
        treeList.add(rootOrg);
        rootOrg.setLft(1L);
        rootOrg.setRht(2L);

        List<TreeNode> childList = new ArrayList<TreeNode>();
        childList.add(rootOrg);
        while (!childList.isEmpty()) {
            System.out.println("-------------------------------------------------------------");
            for (TreeNode treeNode : treeList) {
                System.out.println(treeNode.getId() + "->" + treeNode.getParentId() + "" + treeNode.getCode() + "->" + treeNode.getName() + "->" + treeNode.getLft() + "->" + treeNode.getRht());
            }

            List<TreeNode> _childList = new ArrayList<TreeNode>();
            _childList.addAll(childList);
            childList.clear();

            for (TreeNode _treeModel : _childList) {
                List<TreeNode> tmpChildList = new ArrayList<TreeNode>();
                Long _id = _treeModel.getId();
                Long _pLft = _treeModel.getLft();

                for (TreeNode _org : allOrgsList) {
                    if (_org.getParentId().longValue() == _id.longValue()) {
                        tmpChildList.add(_org);
                    }
                }

                if (tmpChildList.isEmpty()) {
                    continue;
                }

                // 添加子节点
                long _lft = _pLft + 1L;
                for (TreeNode _org : tmpChildList) {
                    _org.setLft(_lft);
                    _org.setRht(_lft + 1L);

                    _lft += 2L;
                }

                // 更新已有节点
                int _incr = tmpChildList.size() * 2;
                for (TreeNode _model : treeList) {
                    if (_model.getLft().longValue() > _pLft.longValue()) {
                        _model.setLft(_model.getLft() + _incr);
                    }
                    if (_model.getRht().longValue() > _pLft.longValue()) {
                        _model.setRht(_model.getRht() + _incr);
                    }
                }

                treeList.addAll(tmpChildList);
                childList.addAll(tmpChildList);
                tmpChildList.clear();
            }
        }

        return treeList;
    }

调用测试:

    public static void main(String[] args) {
        // 加载所有组织
        final List<TreeNode> allOrgsList = new ArrayList<>();
        ...// 此处省去填充代码
          
        // 根组织code
        final String rootOrgCode = "1";
                 
        // 同层按照id増序排序
        allOrgsList.sort((s1, s2) -> {
            return (int)(s1.getId() - s2.getId());
        });

        List<TreeNode> sortedTree = null;
        try {
            sortedTree = sortTreeNode(allOrgsList, rootOrgCode);
        } catch (NoFoundRootNodeException e) {
            e.printStackTrace();
        } catch (EmptyTreeException e) {
            e.printStackTrace();
        }
    }

代用打印结果:

-------------------------------------------------------------
1->01->集团->1->2
-------------------------------------------------------------
1->01->集团->1->10
1001->11001L->中心-01->2->3
1002->11002L->中心-02->4->5
1003->11003L->中心-03->6->7
1004->11004L->中心-04->8->9
-------------------------------------------------------------
1->01->集团->1->26
1001->11001L->中心-01->2->7
1002->11002L->中心-02->8->13
1003->11003L->中心-03->14->19
1004->11004L->中心-04->20->25
10010001->100110010001L->中心-01-区域-01->3->4
10010002->100110010002L->中心-01-区域-02->5->6
10020001->100210020001L->中心-02-区域-01->9->10
10020002->100210020002L->中心-02-区域-02->11->12
10030001->100310030001L->中心-03-区域-01->15->16
10030002->100310030002L->中心-03-区域-02->17->18
10040001->100410040001L->中心-04-区域-01->21->22
10040002->100410040002L->中心-04-区域-02->23->24
-------------------------------------------------------------
1->01->集团->1->60
1001->11001L->中心-01->2->15
1002->11002L->中心-02->16->29
1003->11003L->中心-03->30->45
1004->11004L->中心-04->46->59
10010001->100110010001L->中心-01-区域-01->3->8
10010002->100110010002L->中心-01-区域-02->9->14
10020001->100210020001L->中心-02-区域-01->17->22
10020002->100210020002L->中心-02-区域-02->23->28
10030001->100310030001L->中心-03-区域-01->31->36
10030002->100310030002L->中心-03-区域-02->37->44
10040001->100410040001L->中心-04-区域-01->47->52
10040002->100410040002L->中心-04-区域-02->53->58
1001000100001->100100011001000100001L->中心-01-区域-01-分公司-01->4->5
1001000100002->100100011001000100002L->中心-01-区域-01-分公司-02->6->7
1001000200001->100100021001000200001L->中心-01-区域-02-分公司-01->10->11
1001000200002->100100021001000200002L->中心-01-区域-02-分公司-02->12->13
1002000100001->100200011002000100001L->中心-02-区域-01-分公司-01->18->19
1002000100002->100200011002000100002L->中心-02-区域-01-分公司-02->20->21
1002000200001->100200021002000200001L->中心-02-区域-02-分公司-01->24->25
1002000200002->100200021002000200002L->中心-02-区域-02-分公司-02->26->27
1003000100001->100300011003000100001L->中心-03-区域-01-分公司-01->32->33
1003000100002->100300011003000100002L->中心-03-区域-01-分公司-02->34->35
1003000200001->100300021003000200001L->中心-03-区域-02-分公司-01->38->39
1003000200002->100300021003000200002L->中心-03-区域-02-分公司-02->40->41
1003000200002->100300021003000200003L->中心-03-区域-02-分公司-03->42->43
1004000100001->100400011004000100001L->中心-04-区域-01-分公司-01->48->49
1004000100002->100400011004000100002L->中心-04-区域-01-分公司-02->50->51
1004000200001->100400021004000200001L->中心-04-区域-02-分公司-01->54->55
1004000200002->100400021004000200002L->中心-04-区域-02-分公司-02->56->57

Process finished with exit code 0

实际4个循环结果对应以下4张图:

 

 

posted @ 2021-01-18 21:43  cctext  阅读(185)  评论(0编辑  收藏  举报