二叉排序樹

今天就聊聊這個”五大經典查找“中的最後一個”二叉排序樹“,又叫二叉查找樹。

1. 概念


如圖就是一棵二叉排序樹:


2.實際操作:

    我們都知道,對一個東西進行操作,無非就是增刪查改,接下來我們就聊聊其中的基本操作。

    <1> 插入:相信大家對“排序樹”的概念都清楚了吧,那麼插入的原理就很簡單了。
比如說我們插入一個20到這棵樹中。

首先:20跟50比,發現20是老小,不得已,得要歸結到50的左子樹中去比較。
然後:20跟30比,發現20還是老小。
再然後:20跟10比,發現自己是老大,隨即插入到10的右子樹中。
最後: 效果呈現圖如下:



<2>查找:相信懂得了插入,查找就跟容易理解了。

就拿上面一幅圖來說,比如我想找到節點10.

首先:10跟50比,發現10是老小,則在50的左子樹中找。
然後:10跟30比,發現還是老小,則在30的左子樹中找。
再然後:  10跟10比,發現一樣,然後就返回找到的信號。
        
<3>刪除:刪除節點在樹中還是比較麻煩的,主要有三種情況。

《1》 刪除的是“葉節點20“,這種情況還是比較簡單的,刪除20不會破壞樹的結構。如圖:



《2》刪除”單孩子節點90“,這個情況相比第一種要麻煩一點點,需要把他的孩子頂上去。


《3》刪除“左右孩子都有的節點50”,這個讓我在代碼編寫上糾結了好長時間,問題很直白,我把50刪掉了,誰頂上去了問題,是左孩子呢?還是右 孩子呢?還是另有蹊蹺?這裏我就坦白吧,不知道大家可否知道“二叉樹”的中序遍歷,現在可以當公式記住吧,就是找到右節點的左子樹最左孩子。

比如:首先 找到50的右孩子70。
然後  找到70的最左孩子,發現沒有,則返回自己。
最後  原始圖和最終圖如下。





3.說了這麼多,上代碼說話。

Java代碼  收藏代碼
  1. //二叉搜索樹   
  2. public   class  BinarySearchTree<T  extends  Comparable<?  super  T>>  
  3. {  
  4.   
  5.    /** 二叉排序樹的根 */   
  6.     private  BinaryNode<T> root;  
  7.   
  8.     /**  
  9.      * 構造一棵空樹.  
  10.      */   
  11.     public  BinarySearchTree( )  
  12.     {  
  13.         root = null ;  
  14.     }  
  15.   
  16.     /**  
  17.      * 在二叉搜索樹中插入數據.  
  18.      * @param x the item to insert.  
  19.      */   
  20.     public   void  insert( T x )  
  21.     {  
  22.         root = insert( x, root );  
  23.     }  
  24.   
  25.     /**  
  26.      * 從二叉搜索中刪除數據(節點).  
  27.      * @param x the item to remove.  
  28.      */   
  29.     public   void  remove( T x )  
  30.     {  
  31.         root = remove( x, root );  
  32.     }  
  33.   
  34.     /**  
  35.      * 找最小數據.  
  36.      * @return smallest item or null if empty.  
  37.      */   
  38.     public  T findMin( )  
  39.     {  
  40.         if ( isEmpty( ) )  
  41.             throw   new  UnderflowException( );  
  42.         return  findMin( root ).element;  
  43.     }  
  44.   
  45.     /**  
  46.      * 找最大數據.  
  47.      * @return the largest item of null if empty.  
  48.      */   
  49.     public  T findMax( )  
  50.     {  
  51.         if ( isEmpty( ) )  
  52.             throw   new  UnderflowException( );  
  53.         return  findMax( root ).element;  
  54.     }  
  55.   
  56.     //二叉搜索樹中是否包含x   
  57.     public   boolean  contains( T x )  
  58.     {  
  59.         return  contains( x, root );  
  60.     }  
  61.   
  62.      
  63.     public   void  makeEmpty( )  
  64.     {  
  65.         root = null ;  
  66.     }  
  67.   
  68.      
  69.     public   boolean  isEmpty( )  
  70.     {  
  71.         return  root ==  null ;  
  72.     }  
  73.   
  74.    //中序遍歷輸出二叉搜索樹的內容   
  75.     public   void  printTree( )  
  76.     {  
  77.         if ( isEmpty( ) )  
  78.             System.out.println( "Empty tree"  );  
  79.         else   
  80.             printTree( root );  
  81.     }  
  82.   
  83.     //插入   
  84.     private  BinaryNode<T> insert( T x, BinaryNode<T> t )  
  85.     {  
  86.         if ( t ==  null  )  
  87.             return   new  BinaryNode<T>( x,  null null  );  
  88.           
  89.         int  compareResult = x.compareTo( t.element );  
  90.               
  91.         if ( compareResult <  0  )  
  92.             t.left = insert( x, t.left );  
  93.         else   if ( compareResult >  0  )  
  94.             t.right = insert( x, t.right );  
  95.         else   
  96.            ;  // 重複的,什麼也不做。當然你也可以將重複的數據加入右邊。   
  97.         return  t;  
  98.     }  
  99.   
  100.     //刪除   
  101.     private  BinaryNode<T> remove( T x, BinaryNode<T> t )  
  102.     {  
  103.          //先在樹中查找x      
  104.         if ( t ==  null  )  
  105.             return  t;    // 沒有找到,返回   
  106.           
  107.         int  compareResult = x.compareTo( t.element );  
  108.           // 在左樹中找   
  109.         if ( compareResult <  0  )  
  110.             t.left = remove( x, t.left );  
  111.           //在右樹中找   
  112.         else   if ( compareResult >  0  )  
  113.             t.right = remove( x, t.right );  
  114.         //在樹中找到了節點值爲x的節點   
  115.         else   if ( t.left !=  null  && t.right !=  null  )  // 這個節點有兩個孩子節點   
  116.         {  
  117.             t.element = findMin( t.right ).element;  
  118.             t.right = remove( t.element, t.right );  
  119.         }  
  120.         else //這個節點只有一個孩子節點或沒有孩子節點   
  121.             t = ( t.left != null  ) ? t.left : t.right;  
  122.         return  t;  
  123.     }  
  124.   
  125.    //在二叉搜索樹中找最小值節點   
  126.     private  BinaryNode<T> findMin( BinaryNode<T> t )  
  127.     {  
  128.         if ( t ==  null  )  
  129.             return   null ;  
  130.         else   if ( t.left ==  null  )  
  131.             return  t;  
  132.         return  findMin( t.left );  
  133.     }  
  134.   
  135.     //在二叉搜索樹中找最大值節點   
  136.     private  BinaryNode<T> findMax( BinaryNode<T> t )  
  137.     {  
  138.         if ( t !=  null  )  
  139.             while ( t.right !=  null  )  
  140.                 t = t.right;  
  141.   
  142.         return  t;  
  143.     }  
  144.   
  145.    //是否包含   
  146.     private   boolean  contains( T x, BinaryNode<T> t )  
  147.     {  
  148.         if ( t ==  null  )  
  149.             return   false ;  
  150.               
  151.         int  compareResult = x.compareTo( t.element );  
  152.               
  153.         if ( compareResult <  0  )  
  154.             return  contains( x, t.left );  
  155.         else   if ( compareResult >  0  )  
  156.             return  contains( x, t.right );  
  157.         else   
  158.             return   true ;     // Match   
  159.     }  
  160.   
  161.    //中序遍歷二叉樹   
  162.     private   void  printTree( BinaryNode<T> t )  
  163.     {  
  164.         if ( t !=  null  )  
  165.         {  
  166.             printTree( t.left );  
  167.             System.out.print(t.element+"  " );  
  168.             printTree( t.right );  
  169.         }  
  170.          
  171.     }  
  172.   
  173.     // 二叉搜索樹節點   
  174.     private   static   class  BinaryNode<T>  
  175.     {  
  176.             // Constructors   
  177.         BinaryNode( T theElement )  
  178.         {  
  179.             this ( theElement,  null null  );  
  180.         }  
  181.   
  182.         BinaryNode( T theElement, BinaryNode<T> lt, BinaryNode<T> rt )  
  183.         {  
  184.             element  = theElement;  
  185.             left     = lt;  
  186.             right    = rt;  
  187.         }  
  188.   
  189.         T element;            // The data in the node   
  190.         BinaryNode<T> left;   // Left child   
  191.         BinaryNode<T> right;  // Right child   
  192.     }  
  193.   
  194.   
  195.         
  196.   
  197.         // 測試   
  198.     public   static   void  main( String [ ] args )  
  199.     {  
  200.          //創建二叉排序樹      
  201.         int  list[]={  50 30 70 10 40 90 80 };  
  202.         BinarySearchTree<Integer> bsTree = new  BinarySearchTree<Integer>( );  
  203.          for int  i =  0 ; i<list.length;i++)  
  204.             bsTree.insert( list[i] );  
  205.         
  206.            System.out.println("中序遍歷的原始數據:" );    
  207.             //中序遍歷      
  208.            bsTree.printTree( );  
  209.            System.out.printf("\n--------------------------------" );    
  210.   
  211.          //查找一個節點      
  212.        System.out.printf("\n10在二叉樹中是否包含:"  + bsTree.contains( new  Integer( 10 )));    
  213.      
  214.            System.out.printf("\n---------------------------------" );    
  215.   
  216.         //插入一個節點      
  217.              bsTree.insert(20 );    
  218.      
  219.              System.out.printf("\n20插入到二叉樹,中序遍歷後:" );    
  220.      
  221.              //中序遍歷      
  222.              bsTree.printTree();    
  223.              System.out.printf("\n-----------------------------------\n" );    
  224.   
  225.           System.out.printf("刪除葉子節點 20, \n中序遍歷後:" );    
  226.      
  227.              //刪除一個節點(葉子節點)      
  228.              bsTree.remove(new  Integer( 20 ));    
  229.      
  230.              //再次中序遍歷      
  231.              bsTree.printTree();     
  232.              System.out.printf("\n****************************************\n" );    
  233.      
  234.              System.out.printf("刪除單孩子節點 90, \n中序遍歷後:" );    
  235.      
  236.              //刪除單孩子節點      
  237.              bsTree.remove(new  Integer( 90 ));    
  238.      
  239.              //再次中序遍歷      
  240.              bsTree.printTree();    
  241.              System.out.printf("\n****************************************\n" );    
  242.   
  243.              System.out.printf("刪除根節點 50, \n中序遍歷後:" );    
  244.              //刪除根節點      
  245.              bsTree.remove(new  Integer( 50 ));    
  246.              bsTree.printTree();        
  247.              
  248.     }  
  249. }  
  250.   
  251. public   class  UnderflowException  extends  RuntimeException{}  


運行結果:

D:\java>java   BinarySearchTree
中序遍歷的原始數據:
10  30  40  50  70  80  90
------------------------------------------------
10在二叉樹中是否包含:true
--------------------------------------------------
20插入到二叉樹,中序遍歷後:10  20  30  40  50  70  80  90
-------------------------------------------------
刪除葉子節點 20,
中序遍歷後:10  30  40  50  70  80  90
*************************************************
刪除單孩子節點 90,
中序遍歷後:10  30  40  50  70  80
************************************************
刪除根節點 50,
中序遍歷後:10  30  40  70  80

值的注意的是:二叉排序樹同樣採用“空間換時間”的做法。

突然發現,二叉排序樹的中序遍歷同樣可以排序數組,呵呵,不錯!

PS:  插入操作:O(LogN)。

       刪除操作:O(LogN)。

       查找操作:O(LogN)。

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