二叉查找樹(四)


  接上一篇,讓我們來繼續討論二叉查找樹的基本操作,需要注意的一點是,上篇中的遍歷操作是針對任意一棵二叉樹都可以的,凡是沒提及需要二叉查找樹性質的地方,應該都是滿足二叉樹的操作。上篇主要討論了二叉樹的遍歷操作,這篇,我們來討論二叉查找樹的查找、求最大最小值以及求前驅和后繼等操作。

  • 查找

  二叉查找樹的查找操作可以在O(h)時間內完成,其中h為樹的高度。查找的算法很簡單,根據二叉查找樹的性質,我們先將要比較的元素跟根元素相比較,如果相等則返回,如果大於根結點的key,則繼續在右子樹中查找,如果小於根結點的key值,則在左子樹中查找。這也跟插入過程類似,童鞋們可以想象一下,我就不畫圖了,畫圖很麻煩。

  下面的代碼完成了在樹根為x的樹中查找關鍵字為k的元素,如果存在的話就返回其飲用,不存在,則返回null。

  遞歸查找的代碼為:

 1 /**
 2      * 查找以x為根結點的樹中key的值為k的結點,返回找到的結點或者null
 3      * @author Alfred
 4      * @param x 根結點
 5      * @param k 要查找的整數
 6      * @return 找到的結點或者null
 7      */
 8     private TreeNode treeSearch(TreeNode x, int k){
 9         if(x == null || k == x.getKey()){
10             return x;
11         }
12         if(k < x.getKey()){
13             return treeSearch(x.getLeft(), k);//查左子樹
14         }else{
15             return treeSearch(x.getRight(), k);//查右子樹
16         }
17     }

  非遞歸查找的代碼為:

 

 1 /**
 2      * 非遞歸地查找以x為根結點的樹中key的值為k的結點,返回找到的結點或者null
 3      * @author Alfred
 4      * @param x 根結點
 5      * @param k 要查找的整數
 6      * @return 找到的結點或者null
 7      */
 8     private TreeNode treeSearchNonrecursive(TreeNode x, int k){
 9         while(x != null && k != x.getKey()){
10             if(k < x.getKey()){
11                 x = x.getLeft();
12             }else{
13                 x = x.getRight();
14             }
15         }
16         return x;
17     }
  • 最大值

  根據二叉查找樹的性質,樹中的最大值一定是位於整棵樹的最“右”邊的右孩子,因為,二叉查找樹的性質中說明了,右子樹中的結點都大於或者等於父結點和左子樹。所以求最大值是一個很簡單的操作。代碼如下:

 1 /**
 2      * 找以x為根結點的二叉查找樹中的最大值
 3      * @author Alfred
 4      * @param x 根結點
 5      * @return 最大值結點或者null
 6      */
 7     public TreeNode treeMax(TreeNode x){
 8         while(x.getRight() != null){
 9             x = x.getRight();
10         }
11         return x;
12     }
  • 最小值

  同理,根據二叉查找樹的性質,最小值一定是位於整棵樹中最“左”邊的左孩子,因為,二叉查找樹的性質的性質中說明了,左子樹中的結點都小於或者等於父結點和右子樹。所以跟求最大值對偶的代碼如下:

 1 /**
 2      * 找以x為根結點的二叉查找樹中的最小值
 3      * @author Alfred
 4      * @param x 根結點
 5      * @return 最小值結點或者null
 6      */
 7     public TreeNode treeMin(TreeNode x){
 8         while(x.getLeft() != null){
 9             x = x.getLeft();
10         }
11         return x;
12     }
  • 后繼

  由於在之前的博客里面數結點的定義形式,在處理的時候將樹中相同的結點全都合並了起來,因此,位於樹中不同位置的結點的key值肯定是不同的。因此,我們將求某一個結點后繼結點的操作看成是求大於該結點key值的結點中key值最小的那個結點(有點繞。。。)。

  我們記結點x為輸入結點,y為輸出結點,即y結點是x結點的后繼,在我們這里分兩種情況進行討論:

  1. x結點的右子樹不為空。

    根據二叉查找樹的性質,y肯定是位於x的右子樹上,而且是x的右子樹的最小值。

  2. x結點的右子樹為空。

    如果存在后繼y,則y是x的最低祖先結點,且y的左兒子也是x的祖先。暈了?說的簡單些。如果x結點的右子樹為空,則以x結點為最“右”孩子的子樹t中,x結點一定是這個子樹的最大值,根據二叉查找樹的性質,只有當存在某一結點y,y的左子樹恰好是t的時候,y才是x的后繼。現在回去讀一讀這段開始的話,是不是容易理解多了。

  好了,了解了基本的算法,就讓我貼上代碼吧~

 1     /**
 2      * 找結點x的后繼結點
 3      * @author Alfred
 4      * @param x 結點
 5      * @return x的后繼結點或者null
 6      */
 7     public TreeNode treeSuccessor(TreeNode x){
 8         //第一種情況
 9         if(x.getRight() != null){
10             return treeMin(x.getRight());
11         }
12         //第二種情況
13         TreeNode tmpNode = x.getParent();
14         while(tmpNode != null && x == tmpNode.getRight()){
15             x = tmpNode;
16             tmpNode = tmpNode.getParent();
17         }
18         return tmpNode;
19     }

  這個算法就是按照上面提到的兩種情況來實現的,時間復雜度為O(h),h為樹的高度。

  • 前驅

  求結點的前驅的算法與后繼的算法是對稱的。其時間復雜度也是O(h)。在處理上也分為兩種情況,我就直接上代碼了,有心的童鞋們自己想一下吧~

 1 /**
 2      * 找結點x的前趨結點
 3      * @author Alfred
 4      * @param x 結點
 5      * @return x的前趨結點或者null
 6      */
 7     public TreeNode treePredecessor(TreeNode x){
 8         //第一種情況
 9         if(x.getLeft() != null){
10             return treeMax(x.getLeft());
11         }
12         //第二種情況
13         TreeNode tmpNode = x.getParent();
14         while(tmpNode != null && x == tmpNode.getLeft()){
15             x = tmpNode;
16             tmpNode = tmpNode.getParent();
17         }
18         return tmpNode;
19     }

  嗯。。。本話完,其他操作下回分解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM