1. 二叉查找树
二叉查找树(Binary Search Tree)/ 有序二叉树(ordered binary tree)/ 排序二叉树(sorted binary tree)
1). 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2). 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3). 任意节点的左、右子树也分别为二叉查找树。
4). 没有键值相等的节点(no duplicate nodes)。
public class BinarySearchTree<AnyType extends Comparable<? super AnyType>>{ private static class BinaryNode<AnyType>{ BinaryNode(AnyType theElement){ this(theElement,null,null); } BinaryNode(AnyType theElement, BinaryNode<AnyType> lt, BinaryNode<AnyType>){ element=theElement; left=lt; right= rt; } AnyType element; BinaryNode<AnyType> left; BinaryNode<AnyType> right; } private BinaryNode<AnyType> root; //构造函数 public BinarySearchTree(){ root=null; } public void makeEmpty(){ root=null; } public booolean isEmpty(){ return root==null; } public boolean contains(AnyType x){ return contains(x,root); } public AnyType findMin() { if(isEmpty()) throw new UnderflowException(); return findMin(root).element; } public AnyType findMax() { if(isEmpty()) throw new UnderflowException(); return findMax(root).element; } public void insert(AnyType x){ root=insert(x,root); } public void remove(AnyType x){ root=remove(x,root); } public void printTree(){ if(isEmpty()) System.out.println("Empty tree"); else printTree(root); } //二叉树的contains操作 private boolean contains(AnyType x, BinaryNode<AnyType> t){ if(t==null) return false; //Comparable接口的compareTo方法比较两个值 int compareResult=x.compareTo(t.element); if(compareResult<0) return contains(x,t.left); else if (compareResult>0) return contains(x,t.right); else return true; } //查找最小值节点 private BinaryNode<AnyType> findMin(BinaryNode<AnyType> t){ if(t==null) return null; else if(t.left==null) return t; return findMin(t.left); } //查找最大值节点 private BinaryNode<AnyType> findMax(BinaryNode<AnyType> t){ if(t !=null) while(t.right!=null) t=t.right; return t; } //insert插入,返回对 新树根的引用 private BinaryNode<AnyType> insert(AnyType x, BinaryNode<AnyType> t){ if(t==null) return new BinaryNode<AnyType>(x,null,null); int compareResult=x.compareTo(t.element); if(compareResult<0) t.left=insert(x,t.left); else if(compareResult >0) t.right=insert(x,t.right); else ; return t; } //删除节点 private BinaryNode<AnyType> remove(AnyType x, BinaryNode<AnyType> t){ if(t==null) return t; int compareResult=x.compareTo(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=findMin(t.right).element; t.right=remove(t.element.t.right); } else //只有一个孩子 t=(t.left != null) ? t.left: t.right; return t; } private void printTree(BinaryNode<AnyType> t){ if(t!=null) { printTree(t.left); System.out.println(t.element); printTree(t.right); } } }
2. AVL树
平衡二叉树定义(AVL) / Balanced Binary Tree 或 Height-Balanced Tree:它或者是一棵空树,或者是具有一下性质的二叉查找树-- 它的结点左子树和右子树的深度之差不超过1,而且该结点的左子树和右子树都是一棵平衡二叉树。 平衡因子:结点左子树的深度-结点右子树的深度。(0、1、-1)。
四种情况导致二叉树不平衡:
针对四种种情况可能导致的不平衡,可以通过旋转使之变平衡。有两种基本的旋转:
1)左旋转:将根节点旋转到(根节点的)右孩子的左孩子位置
2)右旋转:将根节点旋转到(根节点的)左孩子的右孩子位置
(1)LL:插入一个新节点到根节点的左子树(Left)的左子树(Left),导致根节点的平衡因子由1变为2
第一列中,在2节点左子树插入D,为了平衡,应该将{2,D}上移一层,A下移一层,以3为根即可,而原来树中B介于3和5之间,因此将B作为5的左子树;
第二列中,在2节点的右子树插入D,同理
(2)RR:插入一个新节点到根节点的右子树(Right)的右子树(Right),导致根节点的平衡因子由-1变为-2
(3)LR:插入一个新节点到根节点的左子树(Left)的右子树(Right),导致根节点的平衡因子由1变为2
(4)RL:插入一个新节点到根节点的右子树(Right)的左子树(Left),导致根节点的平衡因子由-1变为-2
package com.kiritor; /** *二叉平衡树简单实现 */ public class AvlTree< T extends Comparable< ? super T>> { private static class AvlNode< T>{//avl树节点 AvlNode( T theElement ) { this( theElement, null, null ); } AvlNode( T theElement, AvlNode< T> lt, AvlNode< T> rt ) { element = theElement; left = lt; right = rt; height = 0; } T element; // 节点中的数据 AvlNode< T> left; // 左儿子 AvlNode< T> right; // 右儿子 int height; // 节点的高度 } private AvlNode< T> root;//avl树根 public AvlTree( ) { root = null; } //在avl树中插入数据,重复数据复略 public void insert( T x ) { root = insert( x, root ); } //在avl中删除数据,这里并未实现 public void remove( T x ) { System.out.println( "Sorry, remove unimplemented" ); } //在avl树中找最小的数据 public T findMin( ) { if( isEmpty( ) ) System.out.println("树空");; return findMin( root ).element; } //在avl树中找最大的数据 public T findMax( ) { if( isEmpty( ) ) System.out.println("树空"); return findMax( root ).element; } //搜索 public boolean contains( T x ) { return contains( x, root ); } public void makeEmpty( ) { root = null; } public boolean isEmpty( ) { return root == null; } //排序输出avl树 public void printTree( ) { if( isEmpty( ) ) System.out.println( "Empty tree" ); else printTree( root ); } private AvlNode< T> insert( T x, AvlNode< T> t ) { if( t == null ) return new AvlNode< T>( x, null, null ); int compareResult = x.compareTo( t.element ); if( compareResult < 0 ) { t.left = insert( x, t.left );//将x插入左子树中 if( height( t.left ) - height( t.right ) == 2 )//打破平衡 if( x.compareTo( t.left.element ) < 0 )//LL型(左左型) t = rotateWithLeftChild( t ); else //LR型(左右型) t = doubleWithLeftChild( t ); } else if( compareResult > 0 ) { t.right = insert( x, t.right );//将x插入右子树中 if( height( t.right ) - height( t.left ) == 2 )//打破平衡 if( x.compareTo( t.right.element ) > 0 )//RR型(右右型) t = rotateWithRightChild( t ); else //RL型 t = doubleWithRightChild( t ); } else ; // 重复数据,什么也不做 t.height = Math.max( height( t.left ), height( t.right ) ) + 1;//更新高度 return t; } //找最小 private AvlNode< T> findMin( AvlNode< T> t ) { if( t == null ) return t; while( t.left != null ) t = t.left; return t; } //找最大 private AvlNode< T> findMax( AvlNode< T> t ) { if( t == null ) return t; while( t.right != null ) t = t.right; return t; } //搜索(查找) private boolean contains( T x, AvlNode t ) { while( t != null ) { int compareResult = x.compareTo( (T) t.element ); if( compareResult < 0 ) t = t.left; else if( compareResult > 0 ) t = t.right; else return true; // Match } return false; // No match } //中序遍历avl树 private void printTree( AvlNode< T> t ) { if( t != null ) { printTree( t.left ); System.out.println( t.element ); printTree( t.right ); } } //求高度 private int height( AvlNode< T> t ) { return t == null ? -1 : t.height; } //带左子树旋转,适用于LL型 private AvlNode< T> rotateWithLeftChild( AvlNode< T> k2 ) { AvlNode< T> 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 ), k2.height ) + 1; return k1; } //带右子树旋转,适用于RR型 private AvlNode< T> rotateWithRightChild( AvlNode< T> k1 ) { AvlNode< T> 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.right ), k1.height ) + 1; return k2; } //双旋转,适用于LR型 private AvlNode< T> doubleWithLeftChild( AvlNode< T> k3 ) { k3.left = rotateWithRightChild( k3.left ); return rotateWithLeftChild( k3 ); } //双旋转,适用于RL型 private AvlNode< T> doubleWithRightChild( AvlNode< T> k1 ) { k1.right = rotateWithLeftChild( k1.right ); return rotateWithRightChild( k1 ); } // Test program public static void main( String [ ] args ) { AvlTree< Integer> t = new AvlTree< Integer>( ); final int NUMS = 200; final int GAP = 17; System.out.println( "Checking... (no more output means success)" ); for( int i = GAP; i != 0; i = ( i + GAP ) % NUMS ) t.insert( i ); t.printTree( ); System.out.println(t.height(t.root)); } }
3. 伸展树
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它由Daniel Sleator和Robert Tarjan创造,后者对其进行了改进。
假设想要对一个二叉查找树执行一系列的查找操作。为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法,在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。splaytree应运而生。splaytree是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。
伸展树能在O(logn)内完成插入、查找和删除作。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于“伸展”操作。当查找到一个结点后,需要进行“伸展”操作,把这个结点移动到树根。至于伸展操作有两种方式:自底向上和自顶向下。
1)自底向上
所谓的自底向上就是像AVL树那样的“左旋”、“右旋”,多次操作之后把此结点旋转到树根,但这种方式,需要保存查找路径上的各个结点的指针以供旋转之用。
2)自顶向下
当我们沿着树向下搜索某个节点X的时候,我们将搜索路径上的节点及其子树移走。我们构建两棵临时的树──左树和右树。
没有被移走的节点构成的树称作中树。在伸展操作的过程中:
1、当前节点X是中树的根。
2、左树L保存小于X的节点。
3、右树R保存大于X的节点。
开始时候,X是树T的根,左右树L和R都是空的。我们以一个查找2结点的实例,来图解SplayTree的伸展过程。
4.