平衡二叉树是一种特殊的二叉查找树,而二叉查找树又是一种特殊的二叉树,也就是说他们之间的继承关系是:
平衡二叉树 -> 二叉查找树 -> 二叉树
故而我们用来实现平衡二叉树所使用的方式是面向对象地,分步地,利用继承关系分别建立这三种类,而不是直接建立一个平衡二叉树。
一、 第一步,建立抽象类Tree,其内部提供一个静态内部类Node,作为树的节点,并实例化一些方法,例如toString(先序遍历,中序遍历,后序遍历都有),还有树的构造器方法,将从某节点的子孙节点插入和移除某个元素的方法设置为抽象的。代码如下:
import java.util.Comparator;
public abstract class Tree<AnyType extends Comparable<? super AnyType>> {
protected class Node<AnyType> {
AnyType element;
Node<AnyType> left;
Node<AnyType> right;
int height;
public Node(AnyType elem, Node<AnyType> lt, Node<AnyType> rt){
element = elem;
left = lt;
right = rt;
}
public Node(AnyType x){
this(x, null, null);
}
}
protected Node<AnyType> root;
private Comparator<AnyType> cmp;
private int toStringWays = PRD;
private static final int PRD = 0;
private static final int LDR = 1;
private static final int LRD = 2;
public void setToStringWays(int way) {
if (LRD < way || way < PRD)
throw new IllegalStateException();
else toStringWays = way;
}
public Tree(){
root = null;
}
public Tree(Comparator<AnyType> c){
root = null;
cmp = c;
}
protected int height(Node<AnyType> t){
return t == null?-1:t.height;
}
public void makeEmpty(){
root = null;
}
public boolean isEmpty(){
return root == null;
}
protected int myCompare (AnyType lhs, AnyType rhs){
if (cmp != null){
return cmp.compare(lhs, rhs);
}else{
return lhs.compareTo(rhs);
}
}
public String toString(){
if (root == null){
return "Empty Tree";
}else switch (toStringWays){
case PRD: return prdToString(root);
case LDR: return ldrToString(root);
case LRD: return lrdToString(root);
}
return "";
}
private String prdToString(Node<AnyType> t){
String str = "";
if (t != null){
str += t.element+" ";
str += prdToString(t.left);
str += prdToString(t.right);
}
return str;
}
private String ldrToString(Node<AnyType> t){
String str = "";
if (t != null){
str += ldrToString(t.left);
str += t.element+" ";
str += ldrToString(t.right);
}
return str;
}
private String lrdToString(Node<AnyType> t){
String str = "";
if (t != null){
str += lrdToString(t.left);
str += lrdToString(t.right);
str += t.element+" ";
}
return str;
}
public void insert(AnyType x){
root = insert(x, root);
}
public void remove(AnyType x){
root = remove(x, root);
}
abstract protected Node<AnyType> insert(AnyType x, Node<AnyType> t);
abstract protected Node<AnyType> remove(AnyType x, Node<AnyType> t);
}
这样一来,我们的树就可以进行遍历,节点之间的比较,返回节点深度等操作,但是插入和移除功能还没有实现,因为对于不同的二叉树其插入和移除的方式都不尽相同,例如二叉查找树和大堆小堆。
二、二叉查找树继承自二叉树,其特点是,对于任意节点A,A的左树中的任意节点元素要小于节点A中的元素,A的右树中的任意节点元素要大于A中的元素。其插入与移除某个节点也要满足该特点
import java.nio.BufferUnderflowException;
public class BinarySearchTree<AnyType extends Comparable<? super AnyType>> extends Tree<AnyType>{
protected boolean contains(AnyType x, Node<AnyType> t){
if (t == null){
return false;
}
int compareResult = myCompare(x, t.element);
if (compareResult < 0){
return contains(x, t.left);
}else if (compareResult > 0){
return contains(x, t.right);
}else{
return true;
}
}
public AnyType findMin(){
if (isEmpty())
throw new BufferUnderflowException();
return findMin(root).element;
}
public AnyType findMax(){
if (isEmpty())
throw new BufferUnderflowException();
return findMax(root).element;
}
protected Node<AnyType> findMin(Node<AnyType> t){
if (t == null)return null;
else if (t.left == null)return t;
else return findMin(t.left);
}
protected Node<AnyType> findMax(Node<AnyType> t){
if (t != null)
while (t.right!=null)t = t.right;
return t;
}
protected Node<AnyType> insert(AnyType x, Node<AnyType> t){
if (t == null)return new Node<AnyType>(x);
int compareResult = myCompare(x, t.element);
if (compareResult < 0){
t.left = insert(x, t.left);
}else if (compareResult > 0){
t.right = insert(x, t.right);
}
return balance(t);
}
protected Node<AnyType> remove(AnyType x, Node<AnyType> t){
if (t == null)return null;
int compareResult = myCompare(x, t.element);
if (compareResult < 0){
t.left = remove(x, t.left);
}else if (compareResult > 0){
t.right = remove(x, t.right);
}else if (t.left != null && t.right != null){
t.element = findMax(t.right).element;
t = remove(t.element, t.right);
}else{
t = (t.left == null)?t.right:t.left;
}
return balance(t);
}
protected Node<AnyType> balance(Node<AnyType> t){
return t;
}
}
这个类实现了抽象类Tree的插入移除方法,并新增了寻找某节点子树最大元素,和某节点字数最小元素的方法,以及查找某元素的方法。
有的读者可能会疑惑,为什么我们要写一个参数和返回值一模一样的balance()方法。我会在下一段放出解释。
将元素存储在二叉查找树中,则可以利用二叉查找树可以高效地查找元素,但它也存在缺点,若树中的元素普遍集中在根节点的左树或右树,则会导致查找效率低下,最坏的情况就是所有的节点都只有右树,而没有左树,或只有左树,没有右树,这样的查找效率相当于一个列表。
因而我们这里就需要提到平衡二叉树了。这样的话,我们就要重写balance方法,不能单纯返回参数了。
三、平衡二叉树继承自二叉查找树,是对其的升级改良,他的插入操作和移除操作要保证操作结束后根节点左树右树的深度差小于等于一,即平衡状态。 而实现平衡状态,则需要用到单左旋,单右旋,双左旋,双右旋等多种操作,其代码实现如下:
public class AvlTree<AnyType extends Comparable<? super AnyType>> extends BinarySearchTree<AnyType> {
private static final int ALLOWED_IMBALANCE = 1;
protected Node<AnyType> balance(Node<AnyType> t){
if (t == null)return null;
if (height(t.left) - height(t.right) > ALLOWED_IMBALANCE){
if (height(t.left.left) >= height(t.left.right)){
t = rotateWithLeftChild(t);
}else{
t = doubleWithLeftChild(t);
}
}else if (height(t.right) - height(t.left) > ALLOWED_IMBALANCE){
if (height(t.right.right) > height(t.right.left)){
t = rotateWithRightChild(t);
}else{
t = doubleWithRightChild(t);
}
}
t.height = Math.max(height(t.left), height(t.right)) + 1;
return t;
}
private Node<AnyType> rotateWithLeftChild(Node<AnyType> k2){
Node<AnyType> k1 = k2.left;
k2.left = k1.right;
k1.right = k2;
k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
k1.height = Math.max(height(k1.left), height(k2.right)) + 1;
return k1;
}
private Node<AnyType> rotateWithRightChild(Node<AnyType> k1){
Node<AnyType> k2 = k1.right;
k1.right = k2.left;
k2.left = k1;
k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
return k2;
}
private Node<AnyType> doubleWithLeftChild(Node<AnyType> k3){
k3.left = rotateWithRightChild(k3.left);
return rotateWithLeftChild(k3);
}
private Node<AnyType> doubleWithRightChild(Node<AnyType> k3){
k3.right = rotateWithLeftChild(k3.right);
return rotateWithRightChild(k3);
}
}
该类封装了四种旋转操作对应的方法,并重写了继承自二叉查找树的使二叉树平衡的操作。这样就可以实现二叉查找树的高度平衡。