【数据结构】树

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 );
    }
}

(持续更新中~)

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冷冰殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值