二叉樹原理及實現

完整代碼在這裏 https://github.com/zhangjunapk/half_search_tree

 

樹被廣泛使用,比如文件系統,unix上用到了紅黑樹,windows上用到了樹

二分查找樹可以說是一個有序的集合,節點之間用鏈表鏈接起來,可以用二分搜索的方式來對搜索

二分查找樹在寫入上做了一個性能的權衡,每次寫入數據都要遍歷,然後放到合適的位置

是一種很平衡的數據存儲結構

先看看百度百科對二分搜索樹的定義把

 

只要滿足這些條件的樹都能被稱爲二分查找樹

比如可以是這樣子

   這兩個都能被稱爲二分查找樹

 

接下來我們看看爲什麼二分查找樹爲什麼快

如果我們僅僅用鏈表的話,需要遍歷所有節點,直到找到值

加入我們查找6

傳統鏈表就需要順序遍歷節點,直到找到對應的值

 

但是二分搜索樹就不同了,在插入的時候就是排序好的,查找的話直接搜索就行了

在搜索的時候,會判斷當前數值是否一致,然後把要搜索的數值和當前節點進行大小對比,如果大於當前,那就從左邊繼續遞歸遍歷(左邊沒有就結束),右邊也是一樣,這樣就實現了二分查找(二分查找有序數組也有和當前節點對比的邏輯,然後切換索引),二分查找樹比數組方便的地方就是不用再進行一次排序了,因爲插入的時候就保證了順序性。當然這是有代價的,那就是插入的時候沒有數組性能高

接下來看插入代碼的邏輯

 

 public void insert(String k,Integer v){
        //先判斷root是否爲空
        if(root==null){
            root=new Node(k,v);
            System.out.println("直接插到根 ");
            return;
        }

        //接下來遍歷節點
        Node node = new Node(k, v);
        forInsert(node,root);
        System.out.println("--------------------------------");
    }

先看根節點是否爲空,如果爲空就直接初始化根節點,不然執行遞歸插入

 

 //遞歸遍歷,然後插入數值
    private int forInsert(Node insertNode, Node node) {
        //百度百科上說鍵值不能相等
        if(insertNode.getVal().equals(node.getVal()))
            return -1;


        //判斷都空的
        if(node.getLeft()==null&&node.getRight()==null){
            System.out.println("兩邊都空");
            //判斷這個數值和當前節點的大小
            if(insertNode.getVal()<=node.getVal()){
                System.out.println("    插入到"+node.getVal()+"的左邊"+insertNode.getVal());
                node.setLeft(insertNode);
                return 0;
            }
            if(insertNode.getVal()>=node.getVal()){
                System.out.println("    插入到右邊");
                node.setRight(insertNode);
                return 0;
            }
        }

        //判斷要插入的值是否比當前的節點大
        if(insertNode.getVal()<=node.getVal()){
            if(node.getLeft()!=null){
                return forInsert(insertNode,node.getLeft());
            }
            //不然直接insert
            node.setLeft(insertNode);
            return 0;
        }
        if(insertNode.getVal()>=node.getVal()){
            if(node.getRight()!=null){
                return forInsert(insertNode,node.getRight());
            }
            //不然直接insert
            node.setRight(insertNode);
            return 0;
        }
        return -1;
    }

先判斷左右是否都爲空,爲空就把要插入的數值和當前的節點進行對比,然後直接set進去

然後就遞歸插入,進入下一層,然後接着判斷,直到左右都爲空,就接着判斷大小,然後set進去

 

數據插入進去了,我們就需要拿出來,這裏我寫了三種遍歷方式

1.先序遍歷(深度優先)

2.後序遍歷(深度優先)

3.按層遍歷(廣度優先)

先看廣度優先的

 //按層遍歷
    public void eachLayer(){
        System.out.println(root.getVal());
        layEach(root);
    }

這裏先輸出根節點

然後調用遞歸遍歷層的方法

 

//廣度優先(按層遍歷裏面的遞歸)
    private void layEach(Node node) {
        if(node.getLeft()!=null){
            System.out.println("L  "+node.getLeft().getVal());
        }
        if(node.getRight()!=null){
            System.out.println("R  "+node.getRight().getVal());
        }

        if(node.getLeft()!=null){
            layEach(node.getLeft());
        }
        if(node.getRight()!=null){
            layEach(node.getRight());
        }

    }

這裏直接判斷是否爲空,然後直接打印

這裏我這樣寫是有原因的,因爲需要一層一層打印,所有就先輸出,然後進入下一層遞歸

 

這就是調用層級遍歷的輸出

 

然後是先序遍歷

 

  //遞歸先序遍歷
    private void eachBefore(Node node) {
        System.out.println(node.getVal());

        if(node.getLeft()!=null)
            eachBefore(node.getLeft());

        if(node.getRight()!=null)
            eachBefore(node.getRight());
    }

 

先序遍歷是從根節點開始遍歷,然後依次遍歷到左子樹的葉,然後纔會遍歷右子樹(層級調用真好玩)

接下來是後序遍歷

 //遞歸後序遍歷
    private void eachAfter(Node node) {
        System.out.println(node.getVal());

        if(node.getRight()!=null)
            eachBefore(node.getRight());

        if(node.getLeft()!=null)
            eachBefore(node.getLeft());

    }

 

這裏把先序遍歷換一下就行了,先遍歷右子樹,再遍歷左子樹

 

這是完整代碼  https://github.com/zhangjunapk/half_search_tree

 

 

 

 

 

 

 

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