1.基础知识
1.1 树的定义
树(tree)可以用几种方式定义。定义树的一种自然的方式是递归的方式。一棵树是一些节点的集合。这个集合可以是空集;若不是空集,则树由称作根( root )的节点 r r r以及0个或多个非空的(子)树 T 1 T_1 T1, T 2 T_2 T2,…, T k T_k Tk,组成,这些子树中每一棵的根都被来自根r的一条有向的边(edge)所连结。
对任意节点 n i n_i ni, n i n_i ni的深度( depth)为从根到 n i n_i ni的唯一的路径的长。因此,根的深度为0。 n i n_i ni的高(height)是从 n i n_i ni到一片树叶的最长路径的长。因此所有的树叶的高都是0。一棵树的高等于它的根的高。一棵树的深度等于它的最深的树叶的深度;该深度总是等于这棵树的高。
1.2树的实现
第一儿子/下一兄弟表示法
class TreeNode{
Object element;
TreeNode firstchild;
TreeNode nextsibling;
}
2. 二叉树
二叉树是一棵树,其中每个结点的子结点都不能多余两个。
2.1 二叉树的实现
class BinaryNode{
Object element;
BinaryNode left;
BinaryNode right;
}
2.2 表达式树
(后续更新)
3. 二叉查找树
二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有项的值小于X中的项,而它的右子树中所有项的值大于X中的项。
package dataStructure.tree;
import java.nio.BufferUnderflowException;
// 继承了Comparable,为了在下面方便使用compareTo方法
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> rt){
element=theElement;
left=lt;
right=rt;
}
AnyType element;
BinaryNode<AnyType> left;
BinaryNode<AnyType> right;
}
private BinaryNode<AnyType> root; // 二叉树根节点
// 初始化,根节点设为null
public BinarySearchTree(){
root=null;
}
// 置空,根节点置为null
public void makeEmpty(){
root=null;
}
// 判空,判断根节点是否等于null
public boolean isEmpty(){
return root==null;
}
// 判断该二叉搜索树中是否包含x
public boolean contains(AnyType x){
return contains(x,root);
}
// 找到二叉搜索树的最小值
public AnyType findMin(){
if(isEmpty()) throw new BufferUnderflowException();
return findMin(root).element;
}
// 找到二叉搜索树的最大值
public AnyType findMax(){
if(isEmpty()) throw new BufferUnderflowException();
return findMax(root).element;
}
// 打印该二叉树
public void printTree(){
if(isEmpty())
System.out.println("Empty tree");
else
printTree(root);
}
// 判断二叉树t中是否包含x
private boolean contains(AnyType x,BinaryNode<AnyType> t){
if(t==null) return false;
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){
// 在这个方法体内,t只是root的拷贝,t所指向的内容不断被改变,
// 但root指向的内容从未改变,因为root从未被传进来。
if(t!=null)
while(t.right!=null)
t=t.right;
return t;
}
// 插入元素
private BinaryNode<AnyType> insert(AnyType x,BinaryNode<AnyType> t){
if(t==null)
return new BinaryNode<>(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只有左右子树,在右子树中找到最小元素,填入当前结点,
// 然后在右子树中删除该最小元素
t.element=findMin(t.right).element;
t.right=remove(t.element,t.right);
}
else
// 如果t只有左子树,执行赋值操作t=t.left即可
// 如果t只有右子树,执行赋值操作t=t.right即可
// 如果t没有子树,说明t为叶结点,直接删掉即可。t=null
t=(t.left!=null) ? t.left : t.right;
return t;
}
// 打印二叉树t
private void printTree(BinaryNode<AnyType> t){
// 左-根-右:中序遍历
if(t!=null){
printTree(t.left);
System.out.println(t.element);
printTree(t.right);
}
}
}
4. 二叉平衡树(AVL树)
一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树(或者空树)。
AVL树每次插入/删除结点后,都必须重新平衡,以保持AVL树独有的特性。
import java.nio.BufferUnderflowException;
// AvlTree class
//
// CONSTRUCTION: with no initializer
//
// ******************PUBLIC OPERATIONS*********************
// void insert( x ) --> Insert x
// void remove( x ) --> Remove x (unimplemented)
// boolean contains( x ) --> Return true if x is present
// boolean remove( x ) --> Return true if x was present
// Comparable findMin( ) --> Return smallest item
// Comparable findMax( ) --> Return largest item
// boolean isEmpty( ) --> Return true if empty; else false
// void makeEmpty( ) --> Remove all items
// void printTree( ) --> Print tree in sorted order
// ******************ERRORS********************************
// Throws BufferUnderflowException as appropriate
/**
* Implements an AVL tree.
* Note that all "matching" is based on the compareTo method.
* @author Mark Allen Weiss
*/
public class AvlTree<AnyType extends Comparable<? super AnyType>> {
/** 实例域 */
private static class AvlNode<AnyType> { // 子类:AVL树结点
AvlNode( AnyType theElement ) {
this( theElement, null, null );
}
AvlNode( AnyType theElement, AvlNode<AnyType> lt, AvlNode<AnyType> rt ) {
element = theElement;
left = lt;
right = rt;
height = 0;
}
AnyType element; // The data in the node
AvlNode<AnyType> left; // Left child
AvlNode<AnyType> right; // Right child
int height; // Height
}
private AvlNode<AnyType> root; // 根节点
/** 公有方法 */
// AVL树的构造方法
public AvlTree( ) {
root = null;
}
// 向树中插入x,如果树中已有相同的x,则新的插入的x会被忽略
public void insert( AnyType x ) {
root = insert( x, root );
}
// 从树中删除x,如果x没被找到,那就什么也不做
public void remove( AnyType x ) {
root = remove( x, root );
}
// 找到树的最小元素;如果树空,则返回null
public AnyType findMin( ) {
if( isEmpty( ) )
throw new BufferUnderflowException( );
return findMin( root ).element;
}
// 找到树的最大元素;如果树空,则返回null
public AnyType findMax( ) {
if( isEmpty( ) )
throw new BufferUnderflowException( );
return findMax( root ).element;
}
// 判断树中是否包含x
public boolean contains( AnyType x ) {
return contains( x, root );
}
// 将树置空
public void makeEmpty( ) {
root = null;
}
// 判空
public boolean isEmpty( ) {
return root == null;
}
// 检查root是否平衡
public void checkBalance( ) {
checkBalance( root );
}
// 按序打印树的每一个元素
public void printTree( ) {
if( isEmpty( ) )
System.out.println( "Empty tree" );
else
printTree( root );
}
/** 以下为AvlTree的私有方法和私有变量 */
// 不平衡高度的阈值为1;AVL树中任何一个结点的左右子树高度差都不能大于1
private static final int ALLOWED_IMBALANCE = 1;
// t此时刚被插入或删除元素,尚未确定是否平衡,需要执行balance操作
// 这里假设t为从叶结点向上查询,首次出现不平衡的结点
private AvlNode<AnyType> balance( AvlNode<AnyType> t ) {
if( t == null ) // 如果树为空,则不需要平衡
return t;
// 如果t的左子树比右子树高
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 ); // 双次左-右旋转(适用于插入左儿子的右子树)
// 如果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的高度
t.height = Math.max( height( t.left ), height( t.right ) ) + 1;
return t;
}
// 检查root是否平衡
private int checkBalance( AvlNode<AnyType> t ) {
if( t == null )
return -1;
else {
int hl = checkBalance( t.left );
int hr = checkBalance( t.right );
/** height( t.left ) != hl可能为true吗? 不太理解 */
if( Math.abs( height( t.left ) - height( t.right ) ) > 1 ||
height( t.left ) != hl || height( t.right ) != hr )
System.out.println( "OOPS!!" ); // 树不平衡
}
return height( t );
}
// 向子树t中插入x,并返回这个子树新的根节点
private AvlNode<AnyType> insert( AnyType x, AvlNode<AnyType> t ) {
if( t == null )
return new AvlNode<>( 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
; // Duplicate; do nothing
return balance( t );
}
// 从一个子树t中删除x,并返回这个子树新的根节点
private AvlNode<AnyType> remove( AnyType x, AvlNode<AnyType> t ) {
if( t == null )
return t; // Item not found; do nothing
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 ){ // Two children
// 如果删除结点处有两个子树,则在右子树中找到最小元素,放在当前树的根节点,
// 右子树递归实现删除这个被提出来的最小元素;左子树不需要改变
t.element = findMin( t.right ).element;
t.right = remove( t.element, t.right );
}
else
// 如果t有左子树,没有右子树,将t的左子树替代当前结点,
// 如果t没有左子树,有右子树,将t的右子树替代当前结点,
// 左右子树都无,则t=null
t = ( t.left != null ) ? t.left : t.right;
return balance( t ); // AVL树在删除元素后必须实现balance操作
}
// 找到树的最小元素;如果树空,则返回null(私有方法)
private AvlNode<AnyType> findMin( AvlNode<AnyType> t ) {
if( t == null )
return t;
while( t.left != null )
t = t.left;
return t;
}
// 找到树的最大元素;如果树空,则返回null(私有方法)
private AvlNode<AnyType> findMax( AvlNode<AnyType> t ) {
if( t == null )
return t;
while( t.right != null )
t = t.right;
return t;
}
// 判断树t中是否包含x(私有方法)
private boolean contains( AnyType x, AvlNode<AnyType> t ) {
while( t != null ) {
int compareResult = x.compareTo( t.element );
if( compareResult < 0 )
t = t.left;
else if( compareResult > 0 )
t = t.right;
else
return true; // Match
}
return false; // No match
}
// 按序打印树的每一个元素(私有方法)
private void printTree( AvlNode<AnyType> t ) {
// 左-根-右:中序遍历
if( t != null ) {
printTree( t.left );
System.out.println( t.element );
printTree( t.right );
}
}
// 计算树的高度
private int height( AvlNode<AnyType> t ) {
return t == null ? -1 : t.height;
}
// 单次左旋转(左儿子左子树);k2是从下往上首个不平衡结点
private AvlNode<AnyType> rotateWithLeftChild( AvlNode<AnyType> k2 ) {
AvlNode<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 ), k2.height ) + 1;
return k1;
}
// 单次右旋转(右儿子右子树);k1是从下往上首个不平衡结点
private AvlNode<AnyType> rotateWithRightChild( AvlNode<AnyType> k1 ) {
AvlNode<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.right ), k1.height ) + 1;
return k2;
}
// 双次左-右旋转(适用于插入左儿子的右子树)(先单右,再单左)
// k3是从下往上首个不平衡结点
private AvlNode<AnyType> doubleWithLeftChild( AvlNode<AnyType> k3 ) {
k3.left = rotateWithRightChild( k3.left );
return rotateWithLeftChild( k3 );
}
// 双次右-左旋转(适用于插入右儿子的左子树)(先单左,再单右)
// k1是从下往上首个不平衡结点
private AvlNode<AnyType> doubleWithRightChild( AvlNode<AnyType> k1 ) {
k1.right = rotateWithLeftChild( k1.right );
return rotateWithRightChild( k1 );
}
/** 测试程序 */
public static void main( String[] args ) {
AvlTree<Integer> t = new AvlTree<>( ); // 创建一棵空的AVL树
final int SMALL = 20;
final int NUMS = 100; // must be even
final int GAP = 11;
System.out.println( "Checking... (no more output means success)" );
for( int i = GAP; i != 0; i = ( i + GAP ) % NUMS ) {
// System.out.println( "INSERT: " + i );
t.insert( i );
if( NUMS < SMALL )
t.checkBalance( );
}
for( int i = 1; i < NUMS; i+= 2 ) {
// System.out.println( "REMOVE: " + i );
t.remove( i );
if( NUMS < SMALL )
t.checkBalance( );
}
if( NUMS < SMALL ) // 这代表发生了某些意料之外的事情
t.printTree( );
if( t.findMin( ) != 2 || t.findMax( ) != NUMS - 2 )
System.out.println( "FindMin or FindMax error!" );
// t中包含2-NUMS之间的所有偶数,如果有一个偶数不被包含,输出错误信息
for( int i = 2; i < NUMS; i+=2 )
if( !t.contains( i ) )
System.out.println( "Find error1!" );
// t中不包含2-NUMS之间的所有奇数,如果有一个奇数被包含,输出错误信息
for( int i = 1; i < NUMS; i+=2 ) {
if( t.contains( i ) )
System.out.println( "Find error2!" );
}
}
}
5. 堆(优先队列)
优先队列就是有优先级的队列,先从队列里出来的是优先级最高的。优先队列有很多种实现方式,但二叉堆(一般简称为堆)的实现最为经典也最为普遍,所以这里对堆和优先队列不做区分。
堆中:每一个结点都大于它的所有子节点。故其根节点最小。(也可以最大,道理一样)
堆的实现如下:
import java.nio.BufferUnderflowException;
// BinaryHeap class
//
// CONSTRUCTION: with optional capacity (that defaults to 100)
// or an array containing initial items
//
// ******************PUBLIC OPERATIONS*********************
// void insert( x ) --> Insert x
// Comparable deleteMin( )--> Return and remove smallest item
// Comparable findMin( ) --> Return smallest item
// boolean isEmpty( ) --> Return true if empty; else false
// void makeEmpty( ) --> Remove all items
// ******************ERRORS********************************
// Throws UnderflowException as appropriate
// 二叉堆;其元素要求有可比较性
public class BinaryHeap<AnyType extends Comparable<? super AnyType>> {
private static final int DEFAULT_CAPACITY = 10;
private int currentSize; // 堆中元素数量
private AnyType [ ] array; // 存储堆的数组。array[0]处不放元素,从1开始放
public BinaryHeap( ) {
this( DEFAULT_CAPACITY );
}
// 按容量创建一个空堆
public BinaryHeap( int capacity ) {
currentSize = 0;
array = (AnyType[]) new Comparable[ capacity + 1 ];
}
// 按给定数组创建一个堆
public BinaryHeap( AnyType [ ] items ) {
currentSize = items.length;
array = (AnyType[]) new Comparable[ ( currentSize + 2 ) * 11 / 10 ];
int i = 1;
for( AnyType item : items )
array[ i++ ] = item;
buildHeap( );
}
// 判空
public boolean isEmpty( ) {
return currentSize == 0;
}
// 置空
public void makeEmpty( ) {
currentSize = 0;
}
// 往堆中插入x
public void insert( AnyType x ) {
if( currentSize == array.length - 1 )
enlargeArray( array.length * 2 + 1 );
// 向上过滤(小元素上浮)
int hole = ++currentSize;
for( array[ 0 ] = x; x.compareTo( array[ hole / 2 ] ) < 0; hole /= 2 ) // 当hole=1时,条件一定为false
array[ hole ] = array[ hole / 2 ];
array[ hole ] = x;
}
// 按新尺寸扩容数组
private void enlargeArray( int newSize ) {
AnyType [] old = array;
array = (AnyType []) new Comparable[ newSize ];
for( int i = 0; i < old.length; i++ )
array[ i ] = old[ i ];
}
// 找最小元
public AnyType findMin( ) {
if( isEmpty( ) )
throw new BufferUnderflowException( );
return array[ 1 ];
}
// 删除最小元
public AnyType deleteMin( ) {
if( isEmpty( ) )
throw new BufferUnderflowException( );
AnyType minItem = findMin( );
array[ 1 ] = array[ currentSize-- ];
percolateDown( 1 );
return minItem;
}
// 根据当前数组元素创建堆
private void buildHeap( ) {
for( int i = currentSize / 2; i > 0; i-- )
percolateDown( i );
}
// 向下过滤
private void percolateDown( int hole ) {
int child;
AnyType tmp = array[ hole ];
// 最后一个结点的父结点是currentSize/2,这里可以保证hole绝对有孩子结点
for( ; hole * 2 <= currentSize; hole = child ) {
child = hole * 2;
// child不是最后一个结点 and 右孩子小于左孩子
if( child != currentSize &&
array[ child + 1 ].compareTo( array[ child ] ) < 0 )
child++;
// 这里已经筛选出了两个孩子中最小的;
// 如果没有右孩子,左孩子自然最小;如果右孩子大于左孩子,左孩子也最小;如果右孩子小于左孩子,此时child已经指向右孩子
if( array[ child ].compareTo( tmp ) < 0 ) // 父结点大于(最小的)孩子,故父结点与(小)孩子交换位置
array[ hole ] = array[ child ];
// 父结点小于两个孩子
else
break;
}
array[ hole ] = tmp;
}
// Test program
public static void main( String [ ] args )
{
int numItems = 100;
BinaryHeap<Integer> h = new BinaryHeap<>( );
int i = 7,k=0;
for( i = 7; i != 0; i = ( i + 7 ) % numItems ) {
h.insert(i);
k++;
}
System.out.println(k);
for( i = 1; i < numItems; i++ )
if( h.deleteMin( ) != i )
System.out.println( "Oops! " + i );
}
}
(持续更新中~)