zoukankan      html  css  js  c++  java
  • 树以及应用

    树的遍历

    树的遍历主要有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("
    前序遍历:");
            recursivePreOrder(treeA);
          
            System.out.println("
    中序遍历:");
            recursiveInOrder(treeA);
          
            System.out.println("
    后序遍历:");
            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张图:

    基础才是编程人员应该深入研究的问题,比如:
    1)List/Set/Map内部组成原理|区别
    2)mysql索引存储结构&如何调优/b-tree特点、计算复杂度及影响复杂度的因素。。。
    3)JVM运行组成与原理及调优
    4)Java类加载器运行原理
    5)Java中GC过程原理|使用的回收算法原理
    6)Redis中hash一致性实现及与hash其他区别
    7)Java多线程、线程池开发、管理Lock与Synchroined区别
    8)Spring IOC/AOP 原理;加载过程的。。。
    +加关注】。

  • 相关阅读:
    ecshop ajax请求验证captcha(验证码)
    ecshop ajax内置函数Ajax.call
    Execl中函数使用总结
    php应用篇 PHPMailer使用
    Jquery中的选择器
    你的水桶有多满
    在纸上写todo list还是用APP?
    absolute居中
    搬家租房总结
    编译器的作用:将汇编语言翻译成机器语言
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/14295377.html
Copyright © 2011-2022 走看看