樹的遍歷主要有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值。
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); } }
// 加載所有組織 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張圖: