树的遍历
树的遍历主要有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张图:
基础才是编程人员应该深入研究的问题,比如:
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 原理;加载过程的。。。
【+加关注】。