數據結構和算法——二叉樹


1.樹的優勢

有序數組: 查找很快,二分法實現的查找所須要的時間爲O(logN),遍歷也很快,可是在有序數組中插入,刪除卻須要先 找到位置,
       在把數組部分元素後移,效率並不高。


鏈表: 鏈表的插入和刪除都是很快速的,僅僅須要改變下引用值就好了,時間僅爲O(1),可是在鏈表中查找數據卻須要
       遍歷全部的元素, 這個效率有些慢了。

樹的優勢: 樹結合了有序數組和鏈表的優勢,能夠實現快速的查找,也能夠快速的刪除,查找。


樹的一些專用術語:
路徑:
  順着鏈接節點的邊從一個節點到另外一個節點的,所通過的全部節點的順序排列就是路徑。
根:
  根便是樹的頂端,一個樹有且只有一個根,從根到全部節點的路徑有且只有一條。
父節點:
  每個節點的鏈接的上一個節點便是該節點的父節點。
子節點;
  每個節點的向下鏈接的節點便是改節點的子節點
子樹:
  每一個節點均可以認爲是一個樹的根,
葉節點:
  就是沒有子節點的節點
層:
  一個節點的層數,從根節點到該節點有多少代,就是多少層

二叉樹:
  樹種的節點能夠有多個節點,二叉樹是最多隻能有2個節點的樹。二叉樹的兩個節點被稱爲左子節點和右子節點。
 二叉樹的節點是最多有2個子節點,但並非必須有2個子節點。

平衡樹和非平衡樹:
若是一個樹中存在一個或多個的子樹,只有右子節點,或左子節點,那麼這個樹就是非平衡樹。

2.二叉搜索樹:

根節點的左右2個節點,小於根節點在放在左側,大於根節點的放在右側。
 

<1>插入
<2>查找
<3>遍歷
1.中序遍歷
(1)調用自身遍歷節點的左子樹
(2)訪問這個節點
(3)調用自身遍歷節點的右子樹
如上圖遍歷過程:35,38,40,45,50,67,83
2.前序遍歷
(1)訪問這個節點
(2)調用自身遍歷節點的左子樹
(3)調用自身遍歷節點的右子樹
    如上圖遍歷過程:45,38,35,40,67,50,83

3.後序遍歷
(1)調用自身遍歷節點的左子樹
(2)調用自身遍歷節點的右子樹
(3)訪問這個節點
    如上圖遍歷過程:35,40,38,50,83,67,45

<4>刪除
     (1)刪除葉節點(沒有子節點的)
     (2)刪除節點(一個子節點的)
     (3)刪除節點(二個子節點的)
<5>查找最大最小值

3.二叉搜索樹的代碼實現
樹(Tree)的代碼實現:
  Tree
只須要有根節點,便可訪問全部的子節點,這裏能夠簡單的定義該類,只有一個變量Root.Root類型爲Node(節點對象)
該類能夠實現一些操做方法大體以下:
public class Tree {

    Node root;

    public Tree() {
    }

    /**
     *  刪除節點
     * @param key
     */
    public  void deldte( int key){

    }

    /**
     *  查找節點
     *
     * @param key
     */
    public Node find(int key){

      
        return null;
    }

    /**
     *
     *
     * @param key   插入值
     * @param otherdata  插入的其餘數據
     */
    public void insert( int key,int otherdata){
       
    
    }


    /**
     * 遍歷二叉樹
     */

    public void disPlayTree(Node node){
      
}
 

  

 
 

  節點(Node)的代碼實現java

  Node 須要有數據項,有該類對象的左節點,右節點,還能夠包含其餘的數據項。實現大體以下:
node

 

public class Node {
    /**
     * 數據項
     */
    int data;
    /**
     * 其餘數據
     */
    int otherData;
    /**
     * 左節點
     */
    Node leftChild;
    /**
     * 右節點
     */
    Node rightChild;

    public Node() {
    }

}

  

 

  Tree和Node實現後,那麼即可以實現裏面的操做方法了。git

 find查找過程如圖

根據上圖能夠看出查找一個節點,最多比較次數爲Tree的層數,其代碼以下:
 /**
     *  查找節點
     *
     * @param key 查找的值,在該代碼中爲Node.data
     */
    public Node find(int key){

        Node current =root;
        while (current.data!=key){
            /**
             * 小於當前節點的值,去left查找,不然去right
             */
            if(key<current.data){
                current=current.leftChild;
            }else {
                current=current.rightChild;
            }
            /**
             * 沒查找到
             */
            if(current==null){
                return  null;
            }

        }
        return current;
    }
 

  

 
 

  insert插入,插入和查找基本過程差很少,仍然是比較數據項大小,小了放在左側,大了放其右側。github

 

其代碼如:
 /**
     * @param key   插入值 node.data
     * @param otherdata  插入的其餘數據  node.otherdata
     */
    public void insert( int key,int otherdata){
        Node newNode=new Node();
        newNode.data=key;
        newNode.otherData=otherdata;
        if(root==null){
            root=newNode;
        }else{
            Node current=root;
            Node parent;
            while (true)
            {
                parent=current;
                if(key<current.data){
                    current=current.leftChild;
                    if(current==null){
                        parent.leftChild=newNode;
                        return;
                    }
                }else{
                    current=current.rightChild;
                    if(current==null){
                        parent.rightChild=newNode;

                        return;
                    }
                }
            }

        }


    }
 

  

 
 

  遍歷二叉搜索樹數組

  二叉樹的中序遍歷過程是調用自身左子樹,而後訪問節點,在調用自身右子樹。遞歸代碼以下。而前序和後序的遍歷只須要把中序遍歷ide

  中的調用自身的遞歸和訪問節點(就是打印那一行)翻翻順序就ok了。spa

 

 /**
     * 遞歸遍歷二叉樹(中序)
     */

    public void disPlayTree(Node node){
        if(node!=null){
            if(node.leftChild!=null){
                disPlayTree(node.leftChild);
            }

            Log.d("", "二叉樹遍歷: "+node.data);
            if(node.rightChild!=null){
                disPlayTree(node.rightChild);
            }

        }
    }

 

  

 

 

  刪除delete節點。若是待刪除節點是葉節點(沒有子節點),值直接把刪除節點賦值爲null便可。
  若是有一個子節點也簡單,待刪除的節點在刪除節點的左側(右側),則把待刪除節點的子樹賦值給待刪除節點父節點的
  左側(右側)。
  刪除:若是刪除節點右2個子節點,則須要先找到待刪除節點的後續節點,便是比待刪除節點次高的節點。
  如圖:

  刪除過程就是:87位後續節點,爲50的右節點。62爲87的左節點。89位87的右節點。


  刪除:後續節點在左側時:

查找到後續節點是77,則50的右節點爲77,79變成87的左節點,93仍是83的右節點。62變成77的左節點。


  刪除代碼實現:
  1.獲取後續節點
 /**
     * 獲取後序節點
     */
    public  Node  getSuccessor(Node delNode){
        Node successorParent =delNode;
        Node successor=delNode;
        Node current=delNode.rightChild;
        while(current!=null){
            successorParent=successor;
            successor=current;
            current=current.leftChild;
        }
        if(successor!=delNode.rightChild){
            successorParent.leftChild=successor.rightChild;
           successor.rightChild=delNode.rightChild;

        }
        Log.d("二叉樹遍歷", "getSuccessor: "+successor.data);
        return successor;
    }
 

  

 
 

  刪除節點:code

 

  /**
     *  刪除節點
     * @param key
     */
    public  boolean deldte( int key){
        Node current=root;
        Node parent=root;
        boolean isLeftChild=true;
        /**
         * 先把刪除值的Node找出來
         */
        while(current.data!=key){
            parent=current;
            if(key<current.data){
                isLeftChild=true;
                current=current.leftChild;
            }else {
                isLeftChild=false;
                current=current.rightChild;
            }
            if(current==null){
                return  false;
            }

        }         // while結束,查找到刪除節點,就是current

        /**
         * 若是刪除節點是葉節點
         */

        if(current.leftChild==null&&current.rightChild==null){
            if(current==root){
                root=null;
            }else if(isLeftChild){
                parent.leftChild=null;
            }else{
                parent.rightChild=null;
            }

        }

        /**
         * 若是刪除的節點沒有rightChild,只有leftChild
         */

        else if(current.rightChild==null){
            if(current==root){
                root=current.leftChild;
            }
            else  if(isLeftChild){
                parent.leftChild=current.leftChild;
            }
            else {
                parent.rightChild=current.leftChild;
            }
        }

        /**
         * 若是刪除的節點只有rightChild
         */
        else if(current.leftChild==null){
            if(current==root){
                root=current.rightChild;
            }
            else if(isLeftChild){
                parent.leftChild=current.rightChild;
            }else{
                parent.rightChild=current.rightChild;
            }
        }

        /**
         * 若是刪除點有2個節點
         */


        else {

            /**
             * 獲取後續節點
             */
            Node successor=getSuccessor(current);
            if(current==root){
                root=successor;
            }else if( isLeftChild){
                parent.leftChild=successor;
            }else{
                parent.rightChild=successor;
            }
            successor.leftChild=current.leftChild;


        }






        return true;
    }

 

  

 

  找最大最小值:對象



  尋找最大最小值
這個就簡單了,從根節點一直找左節點直到沒有左子節點,那麼這個值就是最小值,反之就是最大值。

4.小結:
代碼:
http://pan.baidu.com/s/1miz8ocC
View Code
 
https://github.com/galibujianbusana/MyErChaShu
View Code
相關文章
相關標籤/搜索