数据结构实战java实现二叉查找树

来源:互联网 发布:淘宝找客户要好评 编辑:程序博客网 时间:2024/05/21 19:25

此方法参考自数据结构与算法分析-java描述

二叉树简介

这里写图片描述
形如这样的上级节点的值大于它的左支所有值,小于它的右支所有值,就叫做二叉树。

二叉树的作用

  • 迅速查找
  • 构造表达式

不平衡二叉树的链表实现

public class MyBinarySearchTree<AnyType extends Comparable<? super AnyType>> {    //ci此内部类构建每个的节点    private static class BinaryNode<AnyType> {        //声明节点数据,左支和右支        AnyType element;        BinaryNode<AnyType> left;        BinaryNode<AnyType> right;        //初始化一个节点        BinaryNode( AnyType theElement ) {            this( theElement, null, null);        }        //创建一个节点        BinaryNode( AnyType theElement, BinaryNode<AnyType> leftTree, BinaryNode<AnyType> rightTree ) {            element = theElement;            left = leftTree;            right = rightTree;        }    }    //声明字段 root 的类型为 BinaryNode<AnyType>    private BinaryNode<AnyType> root;    //初始化二叉查找树    public MyBinarySearchTree() {        root = null;    }    //清空树    public void makeEmpty() {        root = null;    }    //判断树是否为空    public boolean isEmpty() {        return root == null;    }    //判断树是否存在 含有项value的节点    public boolean contains( AnyType value ) {        return contains( value, root );    }    public AnyType findMin() {        try {        if ( isEmpty( ))             throw new UnderflowException("树上没有叶子");        }        catch(UnderflowException e) {            System.out.println(e);        }        return findMin( root ). element;    }    public AnyType findMax() {        try {        if ( isEmpty( ))             throw new UnderflowException("树上没有叶子");        //return findMin( root ). element;                    }        catch(UnderflowException e) {            System.out.println(e);        }        return findMax( root ). element;    }    public void insert ( AnyType value ) {        root = insert( value, root );    }    public void remove( AnyType value ) {        root = remove( value, root );    }    public void printTree() {        printTree( root);          }    //判断 value是否在 tree中    private boolean contains( AnyType value, BinaryNode<AnyType> tree ) {        // 递归退出条件        if ( tree == null )            return false;        //将 value 与 节点的值进行比较        int compareResult = value.compareTo( tree.element );        if ( compareResult < 0 )            return contains( value, tree.left );        else if ( compareResult > 0 )            return contains( value, tree.right );        // 递归退出条件        else             return true;    }    //一直向左支走就是最小值    private BinaryNode<AnyType> findMin( BinaryNode<AnyType> tree ) {        if ( tree == null )            return null;        else if ( tree.left == null )            return tree;        return findMin( tree.left );        }    //一直向右支走就是最大值    private BinaryNode<AnyType> findMax( BinaryNode<AnyType> tree ) {        if ( tree == null )            return null;        else if ( tree.right == null )            return tree;        return findMin( tree.right );                }    //二叉树插入方法,此实现对于重复得数值不进行插入    private BinaryNode<AnyType> insert( AnyType value, BinaryNode<AnyType> tree ) {        if ( tree == null)            return new BinaryNode<AnyType>( value, null, null );        int compareResult = value.compareTo( tree.element);        if ( compareResult < 0)             tree.left = insert( value, tree.left );        else if ( compareResult > 0 )             tree.right = insert( value, tree.right );        // java的语言设定中,如果你创建了一个 有返回值得函数 那么在函数的第一级必须要有return。        else            ;        return tree;       }    //删除节点值后需要对剩下的节点进行重排以维持一个二叉树    private BinaryNode<AnyType> remove( AnyType value, BinaryNode<AnyType> tree ) {        //删除共有4种可能性        //1 删除节点是叶片        //2 删除节点有一个子树        //3 删除节点有俩个子树        //4 删除节点不存在        if ( tree == null )            return tree;        int compareResult = value.compareTo( tree.element );        if (compareResult < 0 )             tree.left = remove( value, tree.left );        else if ( compareResult > 0 )            tree.right = remove( value, tree.right );        //俩个子树的情况        else if ( tree.left != null && tree.right != null) {            //将右子树的最小值替代 tree的数值            tree.element = findMin( tree.right ).element;            //转变为只有一个子树的情况            tree.right = remove( tree.element, tree.right );        }        else             //java 三元表达式            tree = ( tree.left != null) ? tree.left : tree.right;        return tree;    }    //打印树我选择后序遍历这种方式    private void printTree( BinaryNode<AnyType> tree ) {        if (tree == null)            return;        printTree(tree.left);        printTree(tree.right);        System.out.println(tree.element);    }    private static class UnderflowException extends Exception {        public UnderflowException() {        }        public UnderflowException(String message) {            super(message);        }    }   }

测试代码

    //测试代码    public static void main(String[] args) {        MyBinarySearchTree test = new MyBinarySearchTree();        test.insert(3);        test.insert(1);        test.insert(4);        //System.out.println(test.root.right.element);        test.printTree();    }

运行结果

143

写法分析

此种方法实现的二叉树如输入一串连续的排练好的数字,此时树就会都由只有右树的节点构成。并不能很好的进行查找。

一种最古老的二叉平衡树(AVL)的实现

AVL :Adelson-Velskii 和 Landis的名字缩写。

AVL树的实现

在上面的基础上

    //此内部类构建每个AVL树的节点    private static class AvlNode<AnyType> {        AnyType element;        AvlNode<AnyType> left;        AvlNode<AnyType> right;        int height;        //构建一个节点        AvlNode( AnyType theElement ) {            this( theElement, null, null);        }        AvlNode( AnyType theElement, AvlNode<AnyType> lt, AvlNode<AnyType> rt ) {            element = theElement;            left = lt;            right = rt;            height = 0;        }    }    //返回 当前节点的深度    private int height( AvlNode<AnyType> tree ) {        return tree == null ? -1 : tree.height;    }    //avl插入    private AvlNode<AnyType> AVLinsert( AnyType value, AvlNode<AnyType> tree ) {        //递归退出条件        if ( tree == null )            return new AvlNode<AnyType>( value, null, null);        int compareResult = value.compareTo( tree.element);        if( compareResult < 0) {            tree.left = AVLinsert(value, tree.left);            // 如果左树的深度要比右树深度大2,调用旋转。            if (height( tree.left) - height( tree.right) == 2)                // 如果 value 比左树的节点大调用左单旋 ,否则左双旋。                if ( value.compareTo( tree.left.element) < 0 )                    tree = rotateWithLeftChild( tree);                else                    tree = doubleWithleftChild( tree);                     }        else if ( compareResult > 0) {            tree.right = AVLinsert(value, tree.right);            // 如果右树的深度要比左树深度大2,调用旋转。            if (height( tree.right) - height( tree.left) == 2)                // 如果 value 比右树的节点大调用右单旋 ,否则右双旋。                if ( value.compareTo( tree.right.element) < 0 )                    tree = rotateWithRightChild( tree);                else                    tree = doubleWithRightChild( tree);                     }        else            ;        //更新深度值        tree.height = Math.max(height(tree.left), height(tree.right)) + 1;        return tree;    }    //左单旋转    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;      }    //右单旋转    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;      }    //左双旋转    private AvlNode<AnyType> doubleWithleftChild( AvlNode<AnyType> k3 ) {        //转化为俩次单旋转        k3.left = rotateWithRightChild(k3.left);        return rotateWithLeftChild(k3);    }    //右双旋转    private AvlNode<AnyType> doubleWithRightChild( AvlNode<AnyType> k3 ) {        //转化为俩次单旋转        k3.right = rotateWithLeftChild(k3.right);        return rotateWithRightChild(k3);    }    

AVL树的分析

这里写图片描述
这里写图片描述

插入时的旋转操作

这里写图片描述
这里写图片描述
而双旋转可以看做时俩次单旋转