二叉查找樹的遞歸實現及遞歸分析


整體思路:二叉查找樹是一棵樹,對於樹,需要把節點表示出來。由於節點僅僅在樹的內部使用,故采用內部類的形式實現。

樹作為一種ADT,需要屬性及在其上進行的操作。由於大部分樹的操作都是從根節點開始的,因此需要一個根節點屬性,並可根據自己的需求來確定需要實現哪些操作。

對於二叉查找樹,它不是一般的二叉樹,它具有特點:任一節點的左子樹上的節點都比它小,右子樹上的節點都比它大。因此,二叉查找樹的方法實現需要滿足這個特點。

 

一,樹由結點組成

結點的定義如下:

 1     private static class BinaryNode<T>{
 2         T element;
 3         BinaryNode left;
 4         BinaryNode right;
 5         
 6         public BinaryNode(T element) {
 7             this(element, null, null);
 8         }
 9         
10         public BinaryNode(T element, BinaryNode<T>left, BinaryNode<T>right) {
11             this.element = element;
12             this.left = left;
13             this.right = right;
14         }
15     }

 

二,二叉查找樹 類

對於樹,需要有根結點。BinaryNode<T>以靜態內部類方式聲明在BinarySearchTree<T>中。靜態內部類的功能就是:靜態內部類的對象的創建不需要依賴其外部類的對象。

BinarySearchTree采用默認構造方法創建對象,然后調用 insert 方法向樹中添加節點。

public class BinarySearchTree<T extends Comparable<? super T>> {
    
    ......//BinaryNode<T>的聲明
    
    private BinaryNode<T> root;

    ....... // 二叉查找樹中的方法

 

三,二叉查找樹的一些方法的遞歸實現及分析

1) insert(T ele),向二叉查找樹中插入一個元素。插入元素之后,返回樹根節點。

正確版本:

 1 private BinaryNode<T> insert(T ele, BinaryNode<T> root){
 2         if(root == null)
 3             return new BinaryNode<T>(ele);
 4         int compareResult = ele.compareTo(root.element);
 5         if(compareResult > 0)
 6             root.right = insert(ele, root.right);
 7         else if(compareResult < 0)
 8             root.left = insert(ele, root.left);
 9         else
10             ;
11         return root;
12     }

 

錯誤版本一:

 1     private BinaryNode<T> insert1(T ele, BinaryNode<T> root){
 2         if(root == null)
 3             return new BinaryNode<T>(ele);
 4         
 5         int compareResult = ele.compareTo(root.element);
 6         if(compareResult > 0)
 7             return insert(ele, root.right);
 8         else if(compareResult < 0)
 9             return insert(ele, root.left);
10         else
11             return root;
12         
13     }

這種版本的遞歸,返回的是:最后一層遞歸調用時,root所指向的節點。最后一層遞歸調用發生在葉子節點上,故返回的root是最后一個插入的元素。

判斷遞歸調用每一層的返回值時,可按照“壓棧”的順序進行分析。

如,上面程序當運行到第7行時,執行遞歸調用,那么會將當前的環境壓入棧中,保存起來。然后執行下一層的方法調用。

當下面的遞歸調用結束后,程序返回到第7行。繼續向后執行,可以看出第8行的 else if 和 第10行的 else的條件都不成立(因為程序已經在第7行條件成立了)

錯誤版本二:

 1     private BinaryNode<T> insert2(T ele, BinaryNode<T> root){
 2         if(root == null)
 3             return new BinaryNode<T>(ele);
 4         int compareResult = ele.compareTo(root.element);
 5         if(compareResult > 0)
 6             root = insert2(ele, root.right);
 7         else if(compareResult < 0)
 8             root = insert2(ele, root.left);
 9         else
10             ;
11         return root;
12     }


版本二的錯誤和版本一一樣,都沒有建立起父節點與左右孩子節點的連接。因此,最終得到的樹根節點指向的是最后一個插入的元素。

 

2)查找二叉樹中元素最大的節點

正確版本:

 1  /*
 2      * 關於尾遞歸的返回值,該方法只會返回二個值: null 和 'root'
 3      * root 是最后一層遞歸調用時findMax的 root 參數
 4      */
 5     private BinaryNode<T> findMax(BinaryNode<T> root){
 6         if(root == null)
 7             return null;
 8         if(root.right == null)
 9             return root;
10         else
11             return findMax(root.right);
12     }

只有當樹為空時,才返回null。第一個if判斷才會執行,否則第一個if永遠不會執行。每一次遞歸都會使問題的規模縮小---從以root為根的樹,縮小成以root的右孩子為根的樹。

錯誤版本:

1     private BinaryNode<T> findMax1(BinaryNode<T> root){
2         if(root == null)
3             return null;
4         else
5             return findMax(root.right);
6     }
7     

該findMax遞歸版本,不管root是否為空,不管樹中有多少個節點,返回的值都為null。從中可以看出,這種形式的遞歸返回值,由其基准條件來決定。

 

3)先序遍歷的遞歸算法分析

1     public void preOrder(BinaryNode<T> root){
2         if(root == null)
3             return;
4         System.out.print(root.element + " ");
5         preOrder(root.left);
6         preOrder(root.right);
7     }

該方法能夠清晰地分析遞歸的步驟。假設先序遍歷為e d c f g
               e
            d      f

        c             g

1)對於節點e,執行到第4行,輸出e。在第5行,遞歸調用preOrder(d),並把preOrder(e)的相關信息壓入棧中保存。

2)對於節點d,執行到第4行,輸出d。在第5行,遞歸調用preOrder(c),並把preOrder(d)的相關信息壓入棧中保存。

3)對於節點c,執行到第4行,輸出c。在第5行,遞歸調用preOrder(c.left ‘null’),執行到第三行return null.

此時,返回到 2)由於preOrder(d)在棧頂,彈出preOrder(d)的信息。

由於preOrder(d)在它的下一層遞歸前執行到了第5行,故它從第6行繼續向前執行:preOrder(d.right)=preOrder(null)。並又把preOrder(d)的相關信息壓入棧中。

4)執行preOrder(d.right==null)從第3行 return null

此時,又彈出preOrder(d),此時從第6行向下繼續執行,遇到了右大括號,執行結束,返回到上層遞歸處.

5)此時preOrder(e)在棧頂,彈出。從第5行斷點處繼續向下執行:preOrder(e.right)=preOrder(f)

6) 對於節點f,執行到第4行,輸出f。在第5行,遞歸調用preOrder(f.left)=preOrder(null),並把preOrder(f)的相關信息壓入棧保存。

7)執行preOrder(f.left)在第三行返回,彈出preOrder(f)

8) 從preOrder(f)的斷點第5行處向下執行到第6行:preOrder(f.right)=preOrder(g)

9)對於節點g,執行到第4行,輸出g。在第5行遞歸調用preOrder(g.left),並將preOrder(g)的相關信息入棧....

.....

.....

 

二叉查找樹完整實現代碼如下:

 

  1 package c4;
  2 
  3 import java.util.Random;
  4 
  5 import c2.C2_2_8;
  6 
  7 public class BinarySearchTree<T extends Comparable<? super T>> {
  8 
  9     private static class BinaryNode<T> {
 10         T element;
 11         BinaryNode<T> left;
 12         BinaryNode<T> right;
 13 
 14         public BinaryNode(T element) {
 15             this(element, null, null);
 16         }
 17 
 18         public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) {
 19             this.element = element;
 20             this.left = left;
 21             this.right = right;
 22         }
 23 
 24         public String toString() {
 25             return element.toString();
 26         }
 27     }
 28 
 29     private BinaryNode<T> root;
 30 
 31     public BinarySearchTree() {
 32         root = null;
 33     }
 34 
 35     public void makeEmpty() {
 36         root = null;
 37     }
 38 
 39     public boolean isEmpty() {
 40         return root == null;
 41     }
 42 
 43     public boolean contains(T ele) {
 44         return contains(ele, root);
 45     }
 46 
 47     private boolean contains(T ele, BinaryNode<T> root) {
 48         if (root == null)
 49             return false;
 50         int compareResult = ele.compareTo(root.element);
 51         if (compareResult > 0)
 52             return contains(ele, root.right);
 53         else if (compareResult < 0)
 54             return contains(ele, root.left);
 55         else
 56             return true;
 57     }
 58 
 59     public BinaryNode<T> findMax() {
 60         return findMax(root);
 61     }
 62 
 63     /*
 64      * 關於尾遞歸的返回值,該方法只會返回二個值: null 和 'root' root 是最后一層遞歸調用時findMax的 root 參數
 65      */
 66     private BinaryNode<T> findMax(BinaryNode<T> root) {
 67         if (root == null)
 68             return null;
 69         if (root.right == null)
 70             return root;
 71         else
 72             return findMax(root.right);
 73     }
 74 
 75     public BinaryNode<T> findMin() {
 76         return findMin(root);
 77     }
 78 
 79     private BinaryNode<T> findMin(BinaryNode<T> root) {
 80         if (root == null)
 81             return null;
 82         if (root.left == null)
 83             return root;
 84         else
 85             return findMin(root.left);
 86     }
 87 
 88     public void insert(T ele) {
 89         root = insert(ele, root);// 每次插入操作都會'更新'根節點.
 90     }
 91 
 92     private BinaryNode<T> insert(T ele, BinaryNode<T> root) {
 93         if (root == null)
 94             return new BinaryNode<T>(ele);
 95         int compareResult = ele.compareTo(root.element);
 96         if (compareResult > 0)
 97             root.right = insert(ele, root.right);
 98         else if (compareResult < 0)
 99             root.left = insert(ele, root.left);
100         else
101             ;
102         return root;
103     }
104 
105     public void preOrder(BinaryNode<T> root) {
106         if (root == null)
107             return;
108         System.out.print(root.element + " ");
109         preOrder(root.left);
110         preOrder(root.right);
111     }
112 
113     public void inOrder(BinaryNode<T> root) {
114         if (root == null)
115             return;
116         inOrder(root.left);
117         System.out.print(root.element + " ");
118         inOrder(root.right);
119     }
120 
121     public int height() {
122         return height(root);
123     }
124 
125     private int height(BinaryNode<T> root) {
126         if (root == null)
127             return -1;// 葉子節點的高度為0,空樹的高度為1
128 
129         return 1 + (int) Math.max(height(root.left), height(root.right));
130     }
131 
132     public static void main(String[] args) {
133         BinarySearchTree<String> tree = new BinarySearchTree<>();
134         tree.insert("e");
135         tree.insert("d");
136         tree.insert("c");
137         tree.insert("f");
138         tree.insert("g");
139 
140         System.out.println("contains g? " + tree.contains("g"));
141         System.out.println("contains h? " + tree.contains("h"));
142         System.out.println("max node: " + tree.findMax().toString());
143         tree.preOrder(tree.root);
144         System.out.println();
145         tree.inOrder(tree.root);
146         System.out.println("\nheight: " + tree.height());
147 148

 


免責聲明!

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



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