樹以及應用

樹的遍歷

樹的遍歷主要有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張圖:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章