对于大量的输入数据,链表的线性访问时间太慢,不宜使用。树保证了大部分操作的运行时间平均为O(logN)。二叉查找树是两种库集合类TreeSet和TreeMap实现的基础,对于长的指令序列,基本上给出每种操作的O(logN)运行时间。
1.基础知识
一棵树是N个节点和N-1条边的集合,其中的一个节点叫做根。
树叶:没有儿子的节点;
兄弟:具有相同父亲的节点;
路径:路径的长是该路径上的边的条数;每个节点到它自己有长为0的路径;一棵树中从根到每个节点恰好存在一条路径。
节点深度:从根到节点的唯一路径的长;根的深度为0;一棵树的深度等于它最深树叶的深度,该深度总是等于这棵树的高。
节点高度:从节点到一片树叶的最长路径的长;树叶的高都是0;一棵树的高等于它的根的高。
1.1树的实现
1.2树的遍历及应用
先序遍历:
中序遍历:
后序遍历:
2.二叉树
一颗平均二叉树的深度要比节点个数N小的多,其平均深度为O(sqrt(N)).
对于二叉查找树,其深度平均值为O(logN).(极端情况可以达到N-1)
3.二叉查找树
性质:对于树中的每个节点X,它的左子树中所有项的值小于X中的项,右子树中所有项的值大于X中的项。

import java.util.Comparator;

/**
 * 二叉查找树性质: 
 *   对于树中的每个节点t,他的左子树中所有项的值小于t中的项,而他的右子树中所有项的值大于t中的项。
 * 平均情况分析:  
 *   假设所有的插入序列都是等可能的,则树的所有节点的平均深度为O(logN).
 *   操作的平均运行时间O(logN).
 * @author yulong
 *
 * @param <Type>
 */
public class BinarySearchTree<Type> {

    private static class BinaryNode<Type>{
        private Type element;
        private BinaryNode<Type> left;
        private BinaryNode<Type> right;

        public BinaryNode(Type x){
            this(x,null,null);
        }
        public BinaryNode(Type x,BinaryNode<Type> lft,BinaryNode<Type> rht){
            element = x;
            left = lft;
            right = rht;
        }
    }

    private BinaryNode<Type> root;//根节点
    private int size;//节点个数
    private Comparator<? super Type> cmp;

    public BinarySearchTree(){
        root = null;
        size = 0;
    }
    public BinarySearchTree(Comparator<? super Type> c){
        root = null;
        size = 0;
        cmp = c;
    }
    //自定义比较器方法
    /*
     *方式一:对Comparable型的对象进行比较  
       *int compareResult = x.compareTo(t.element);  
       *方式二:使用一个函数对象而不是要求这些项是Comparable的  
     */
    private int myCompare(Type x,Type y){
        if(cmp!=null){
            return cmp.compare(x, y);
        }else{
            return ((Comparable)x).compareTo(y);
        }
    }

    public int size(){
        return size;
    }
    public void makeEmpty(){
        root = null;
        size = 0;
    }
    public boolean isEmpty(){
        return root==null;
    }

    public boolean contains(Type x){
        return contains(x,root);  //通过调用私有函数的方法,可以避免直接操作root产生的对原树的误操作
    }
    public Type findMin(){
        return findMin(root).element;
    }
    public Type findMax(){
        return findMax(root).element;
    }
    public void insert(Type x){
        root = insert(x,root);
        size++;
    }
    public void remove(Type x){
        if(contains(x)){
            root = remove(x,root);
            size--;
        }else{
            //throw new java.util.NoSuchElementException();
        }
    }
    public void printTree(){
        printTree(root);
        System.out.println();
    }

    private boolean contains(Type x,BinaryNode<Type> t){
        if(t==null){
            return false;
        }
        int compareResult = myCompare(x,t.element);
        if(compareResult>0){
            return contains(x,t.right); 
        }
        else if(compareResult<0){
            return contains(x,t.left);
        }
        else{
            return true;
        }
    }
    /**
     * 递归,查找二叉查找树最小值
     * @param t 根节点
     * @return 含最小值的节点
     */
    private BinaryNode<Type> findMin(BinaryNode<Type> t){
        if(t==null){
            return null;
        }
        else if(t.left==null){
            return t;
        }
        else{
            return findMin(t.left);
        }
    }
    /**
     * 非递归,查找二叉查找树最大值
     * @param t 根节点
     * @return 含最大值的节点
     */
    private BinaryNode<Type> findMax(BinaryNode<Type> t){
        while(t.right!=null){
            t = t.right;
        }
        return t;
    }
    /**
     * 插入节点
     * @param x 待插入值
     * @param t 根节点引用
     * @return 根节点
     */
    private BinaryNode<Type> insert(Type x,BinaryNode<Type> t){
        if(t==null){
            return new BinaryNode<Type>(x,null,null);
        }
        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);
        }
        else{

        }
        return t;
    }
    /**
     * 删除节点:
     * (1)删除叶子节点 
     * (2)删除含有一个儿子节点的子节点
     * (3)删除含有两个儿子节点的子节点
     * @param x 待删除元素
     * @param t 根节点
     * @return 根节点
     */
    private BinaryNode<Type> remove(Type x,BinaryNode<Type> t){
        if(t==null){
            return t;
        }
        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 = findMin(t.right).element;
            t.right = remove(t.element,t.right);
        }
        else{
            t = (t.left!=null)?t.left:t.right;
        }
        return t;
    }
    /**
     * 按从小到大顺序打印输出此二叉查找树(中序遍历)
     * @param 树根节点
     */
    private void printTree(BinaryNode<Type> t){
        if(t==null){
            System.out.println("空树");
        }
        if(t.left!=null && t.right!=null){
            printTree(t.left);
            System.out.print(t.element+" ");
            printTree(t.right);
        }
        else if(t.left!=null && t.right==null){
            printTree(t.left);
            System.out.print(t.element+" ");
        }
        else if(t.right!=null){
            System.out.print(t.element+" ");
            printTree(t.right);
        }
        else{
            System.out.print(t.element+" ");
        }   
    }
    }

4.AVL树—带有平衡条件的二叉查找树
性质:每个节点的左子树和右子树高度最多差1的二叉查找树(空树高度为-1)。
保证树的深度须是O(logN).
已知节点数N: logN<高度<1.44log(N+2)-1.328.
已知树高h : 最少节点数 s(h)=s(h-1)+s(h-2)+1.

/**
 * AVL树(平衡二叉查找树)性质:
 *    每个节点的左子树与右子树的高度最多差1(空树高为-1)
 *    AVL树的高度最多为1.44log(N+2)-1.328,但实际上只略大于logN。
 *    已知节点数N: logN<树高<1.44log(N+2)-1.328;
 *    已知树高h:  最少节点数S(h)=S(h-1)+S(h-2)+1.
 * 插入节点导致a节点的不平衡(a点两棵子树高度差2),4种情况:
 *   (1)对a节点的左儿子的左子树进行插入,——>LL旋转
 *   (2)对a节点的左儿子的右子树进行插入,——>LR旋转
 *   (3)对a节点的右儿子的右子树进行插入,——>RR旋转
 *   (4)对a节点的右儿子的左子树进行插入。——>RL旋转
 * @author yulong
 *
 * @param <Type>
 */
public class AVLTree<Type extends Comparable<? super Type>>{

    private static class AVLNode<Type>{
        public Type data;
        public int height; //记录树的高度
        public AVLNode<Type> left;
        public AVLNode<Type> right;

        public AVLNode(Type x){
            this(x,null,null);
        }
        public AVLNode(Type x,AVLNode<Type> lt,AVLNode<Type> rt){
            data = x;
            left = lt;
            right = rt;
            height = 0;
        }
    }

    private int size; //节点个数
    private AVLNode<Type> root;

    public AVLTree(){
        clear();
    }
    /**
     * 初始化、清空树
     */
    public void clear(){
        size = 0;
        root = null;
    }
    /**
     * 求树的节点个数
     * @return 大小
     */
    public int size(){
        return size;
    }
    /**
     * 求AVL树高
     * @return 树高
     */
    public int height(){
        return height(root);
    }
    /**
     * 判断树是否为空
     * @return boolean
     */
    public boolean isEmpty(){
        return root==null;
    }
    /**
     * 判断是否包含指定元素
     * @param x 待判断元素
     * @return boolean
     */
    public boolean contains(Type x){
        return contains(x,root);
    }
    /**
     * 返回树中最小值
     * @return 最小元素
     */
    public Type findMin(){
        return findMin(root);
    }
    /**
     * 返回树中最大值
     * @return 最大元素
     */
    public Type findMax(){
        return findMax(root);
    }
    /**
     * 向AVL树中添加元素
     * @param x 待添加元素
     */
    public void insert(Type x){
        root = insert(x,root);
        size++;
    }
    /**
     * 从AVL树中删除给定元素
     * @param x 待删除元素
     */
    /*public void remove(Type x){
        if(contains(x)){
            size--;
            root = remove(x,root);
        }else{

        }
    }*/
    /**
     * 按从小到大顺序打印输出AVL树中的元素(中序遍历)
     */
    public void printTree(){
        if(isEmpty()){
            System.out.print("空树");
        }else{
            printTree(root);
        }
        System.out.println();
    }
    /**
     * 求子树t的高度
     * @param t 子树根节点
     * @return 子树高度
     */
    private int height(AVLNode<Type> t){
        return t==null ? -1 : t.height;
    }
    /**
     * 判断以结点t为根节点的子树是否包含指定的元素x
     * @param x 待判断元素
     * @param t 子树根节点
     * @return boolean
     */
    private boolean contains(Type x,AVLNode<Type> t){
        if(t==null){
            return false;
        }
        int compareResult = x.compareTo(t.data);
        if(compareResult<0){
            return contains(x,t.left);
        }else if(compareResult>0){
            return contains(x,t.right);
        }else{
            return true;
        }
    }
    /**
     * 非递归方式查找子树t中最小元素
     * @param t 子树t根节点
     * @return 最小元素
     */
    private Type findMin(AVLNode<Type> t){
        while(t.left!=null){
            t = t.left;
        }
        return t.data;
    }
    /**
     * 递归方式查找子树t中最大值
     * @param t 子树t根节点
     * @return 最大元素
     */
    private Type findMax(AVLNode<Type> t){
        if(t==null){
            return null;
        }else if(t.right==null){
            return t.data;
        }else{
            return findMax(t.right);
        }
    }
    /**
     * 添加元素
     * @param x 待添加元素
     * @param t 等待添加元素的树
     * @return 新的树根节点
     */
    private AVLNode<Type> insert(Type x,AVLNode<Type> t){ //树根可能变换,返回新根节点
        if(t==null){
            return new AVLNode<Type>(x,null,null);
        }
        int compareResult = x.compareTo(t.data);
        if(compareResult<0){
            t.left = insert(x,t.left);
            if(height(t.left)-height(t.right)==2){
                if(x.compareTo(t.left.data)<0){
                        //LL型单旋转,把树高恢复到插入前水平
                    t = rotateWithLeftChild(t);
                }else{
                        //LR型双旋转,把树高度恢复到插入前水平
                    t = doubleWithLeftChild(t);
                }
            }
        }else if(compareResult>0){
            t.right = insert(x,t.right);
            if(height(t.right)-height(t.left)==2){
                if(x.compareTo(t.right.data)>0){
                        //RR型单旋转,把树高恢复到插入前水平
                    t = rotateWithRightChild(t);
                }else{
                        //RL型双旋转,把树高度恢复到插入前水平
                    t = doubleWithRightChild(t);
                }
            }
        }else{

        }
        /*
         * 以上的旋转操作只是在旋转方法内部改变了树的高度,并没有实际改变返回节点在外部的高度。
         */
        //!!更新树高(起初忘记这一步,导致不能生成AVL树)
        t.height = Math.max(height(t.left), height(t.right))+1;
        return t; //返回AVL树新的根节点
    }
    private AVLNode<Type> rotateWithLeftChild(AVLNode<Type> t){
        AVLNode<Type> t1 = t.left;
        t.left = t1.right;
        t1.right = t;
        t.height = Math.max(height(t.left), height(t.right))+1;
        t1.height = Math.max(height(t1.left),t.height)+1;
        return t1;
    }
    private AVLNode<Type> rotateWithRightChild(AVLNode<Type> t){
        AVLNode<Type> t1 = t.right;
        t.right = t1.left;
        t1.left = t;
        t.height = Math.max(height(t.left), height(t.right))+1;
        t1.height = Math.max(height(t1.right),t.height)+1;
        return t1;
    }
    private AVLNode<Type> doubleWithLeftChild(AVLNode<Type> t){
        t.left = rotateWithRightChild(t.left);
        return rotateWithLeftChild(t);
    }
    private AVLNode<Type> doubleWithRightChild(AVLNode<Type> t){
        t.right = rotateWithLeftChild(t.right);
        return rotateWithRightChild(t);
    }
    /**
     * 删除元素
     * @param x
     * @param t
     * @return
     */
    private AVLNode<Type> remove(Type x,AVLNode<Type> t){
        ...
    }
    /**
     * 从小到大输出AVL树中元素(中序遍历)
     * @param t 待输出子树的根节点
     */
    private void printTree(AVLNode<Type> t){
        if(t!=null){
            printTree(t.left);
        System.out.print(t.data+" ");
        printTree(t.right);
        }
    }
    }

5.伸展树

摊还运行时间:当M次操作的序列总的最坏情形运行时间为O(Mf(N))时,它的摊还运行时间为O(f(N)).

一棵伸展树每次操作的摊还代价为O(logN).

性质:保证从空树开始连续M次对树的操作最多花费O(MlogN)时间。

当一个节点被访问后,它就要经过一系列的AVL树的旋转被推到根上。

伸展树不要求保留高度或平衡信息。

5.1展开

两种旋转情况:

b java 树实现 树 java 算法_AVL树


展开操作不仅将访问的节点移动到根处,而且还把访问路径上的大部分节点的深度大致减少一半。

删除:可通过访问要被删除的节点来执行删除操作。此操作将节点上推到根处,如果删除该节点,则得到两棵子树TL和TR,找到TL中的最大元素,该元素被移到TL的根,此时,使TR变为其右儿子,从而完成删除。

6.树的遍历

对于二叉查找树,其中序遍历与后续遍历总的运行时间都为O(N).

7.B树

原则上B树保证只有少数的磁盘访问。

阶为M的B树有下列特性:

1)数据项存储在树叶上

2)非叶节点存储直到M-1个关键字来指示搜索方向;关键字i代表子树i+1中的最小关键字

3)树的根或者是一片树叶,或者其儿子数在2和M之间

4)除根外,所有非树叶节点的儿子数在[M/2]和M之间

5)所有的树叶都在相同的深度上,并有[L/2]~L个数据项。

b java 树实现 树 java 算法_AVL树_02


B树增加高度的唯一方式:当分裂树根时,得到两个树根,此时建立一个新的根,这个根以分裂得到的两个树根作为它的两个儿子(这是准许树根可以至少有两个儿子的原因)。

B树降低高度的唯一方式:若被删元所在树叶的数据项已经是最小值,(1)可以在临界点没有达到最小值时领养一个临项,(2)如果临界点已经达到最小值,可以与该临界点联合形成一片满叶,这意味着父节点失去一个儿子,这个过程可以上行到根,如果领养过程使得根只剩下一个儿子,那么删除该根,让它的儿子作为树的新根。

8.标准库中的集合与映射

8.1set接口

8.2Map接口

8.3TreeSet类和TreeMap类的实现

Java要求其支持的基本add,remove和contains操作以对数最坏情形时间完成。因此基本实现方法是平衡二叉查找树,但一般并不使用AVL树,而经常使用自顶向下的红黑树。