整体思路:二叉查找树是一棵树,对于树,需要把节点表示出来。由于节点仅仅在树的内部使用,故采用内部类的形式实现。
树作为一种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 }