二叉查找树的递归实现及递归分析

整体思路:二叉查找树是一棵树,对于树,需要把节点表示出来。由于节点仅仅在树的内部使用,故采用内部类的形式实现。

树作为一种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

 

posted @ 2016-04-14 11:38  hapjin  阅读(4879)  评论(0编辑  收藏  举报