2020-09-05

平衡二叉树

平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。平衡树可以完成集合的一系列操作, 时间复杂度和空间复杂度相对于“2-3树”要低,在完成集合的一系列操作中始终保持平衡,为大型数据库的组织、索引提供了一条新的途径。


平衡二叉树的实现方法:


左旋转:

满足条件:如果它的右子树的左子树的高度大于它的右子树高度,先对这个结点的右子结点进行右旋转

(1)创建一个新的结点newNode,值等于当前根结点的值

(2)把新结点的左子树设置为当前结点的左子树:newNode.left = this.left

(3)把新结点的右子树设置为当前结点的右子树的左子树:newNode.right = this.right.left

(4)把当前结点的值替换为右子结点的值:this.value = this.right.value

(5)把当前结点的右子树设置为右子树的右子树:this.right = this.right.right

(6)把当前结点的左子树设置为新结点:this.left = this.newNode

在这里插入图片描述


右旋转:


满足条件:如果它的左子树的右子树的高度大于它的左子树高度,先对这个结点的左结点进行左旋转
思路:

(1)创建一个新的结点newNode,newNode的值等于当前根结点的值

(2)把新结点的右子树设置为当前结点的右子树:newNode.right = this.right

(3)把新结点的左子树设置为当前结点的左子树的右子树:newNode.left = this.left.right

(4)把当前结点的值替换为左子结点的值:this.value = this.left.value

(5)把当前结点的左子树设置为当前结点的左子树的左子树:this.left = this.left.left

(6)把当前结点的右子树设置为新的结点:this.right.value = newNode

在这里插入图片描述

实现代码:

package com.avl;


import java.security.PublicKey;


public class AVLTreeDemo {
    public static void main(String[] args) {
        int[] arr = {10,7,11,6,8,9};
        AVLTree avlTree = new AVLTree();
        for (int i = 0; i < arr.length; i++) {
            avlTree.addNode(new Node(arr[i]));
        }
        System.out.println(avlTree.getRoot().height());
        System.out.println("树的根结点:" + avlTree.getRoot());
        System.out.println("树的左子树高度:" + avlTree.getRoot().leftHeight());
        System.out.println("树的右子树高度:" + avlTree.getRoot().rightHeight());


    }
}

//创建平衡二叉树
class AVLTree {
    private Node root;

    public Node getRoot() {
        return root;
    }

    public void setRoot(Node root) {
        this.root = root;
    }

    //查找需要删除结点的方法
    public Node searchTarget(int value) {
        if (root == null)
            return null;
        else
            return root.searchTarget(value);
    }

    //查找需要删除结点的父节点(parentNode)
    public Node searchparentNode(int value) {
        if (root == null)
            return null;
        else
            return root.searchParentNode(value);
    }

    //右子树向左删除最小结点
    public int delRightTreeMin(Node node) {
        //定义一个辅助变量指针
        Node target = node;
        while (target.getLeft() != null) {
            //向左递归查找最小的结点
            target = target.getLeft();
        }
        //删除最小结点
        delNode(target.getValue());
        return target.getValue();//返回当前最小的结点
    }

    //删除结点方法
    public void delNode(int value) {
        if (root == null)
            return;
        else {
            //1、查找需要删除的结点(targetNode)
            Node targetNode = searchTarget(value);
            if (targetNode == null)//此时没有找到需要删除的结点
                return;
            //如果此时二叉排序树只有一个结点(根结点),直接删除
            if (root.getLeft() == null && root.getRight() == null) {
                root = null;
                return;
            }
            //2、查找targetNode的父结点(parentNode)
            Node parentNode = searchparentNode(value);
            //一、如果要删除的结点是叶子结点
            if (targetNode.getLeft() == null && targetNode.getRight() == null) {
                //此时targetNode为叶子结点,判断targetNode是父节点的左子结点或右子结点
                //此时targetNode是parentNode的左子结点
                if (parentNode.getLeft() != null && parentNode.getLeft().getValue() == value)
                    parentNode.setLeft(null);
                    //此时targetNode是parentNode的左子结点
                else if (parentNode.getRight() != null && parentNode.getRight().getValue() == value)
                    parentNode.setRight(null);
            }
            //二、如果要删除的是有两颗子树的结点
            else if (targetNode.getLeft() != null && targetNode.getRight() != null) {
                int minVal = delRightTreeMin(targetNode.getRight());//从右子树删除最小的结点
                targetNode.setValue(minVal);
            }
            //三、如果要删除的是只有一颗子树的结点
            else {
                if (targetNode.getLeft() != null) {//如果要删除的结点有左子结点
                    if (parentNode != null) {
                        //targetNode是parentNode的左子结点
                        if (parentNode.getLeft().getValue() == value)
                            parentNode.setLeft(targetNode.getLeft());
                            //targetNode是parentNode的右子结点
                        else {
                            parentNode.setRight(targetNode.getLeft());
                        }
                    } else//此时删除的是根结点
                    {
                        root = targetNode.getLeft();
                    }

                } else //如果删除的结点有右子结点
                {
                    if (parentNode != null) {
                        //targetNode是parentNode的左子结点
                        if (parentNode.getLeft().getValue() == value) {
                            parentNode.setLeft(targetNode.getRight());
                        } else//targetNode是parentNode的右子结点
                            parentNode.setRight(targetNode.getRight());
                    } else {//此时删除的为根结点
                        root = targetNode.getRight();
                    }
                }
            }


        }

    }
    //添加结点方法
    public void addNode(Node node) {
        if (root == null)//如果root为null,让root指向node
            root = node;
        else
            root.addNode(node);
    }
    //中序遍历方法
    public void infixOrder()
    {
        if (root != null)
            root.infixOrder();
        else
            System.out.println("树为空");
    }
}
    //创建Node结点
    class Node {
        int value;
        Node left;
        Node right;

        public Node(int value) {
            this.value = value;
        }



        //左旋转的方法
        public void leftRotate()
        {
            //1、创建新的结点,以当前根结点的值创建
            Node newNode = new Node(this.value);
            //2、把新的结点的左子树设置为当前结点的左子树
            newNode.left = this.left;
            //3、把新的结点的右子树设置为当前结点的右子树的左子树
            newNode.right = this.right.left;
            //4、把当前结点的值替换为右子结点的值
            this.value = this.right.value;
            //5、把当前结点的右子树设置为当前结点右子树的右子树
            this.right = this.right.right;
            //6、把当前结点的左子树设置为新结点
            this.left = newNode;
        }

        //右旋转的方式
        public void rightRotate()
        {
            //创建一个新的结点newNode,newNode的值等于当前根结点的值
            Node newNode = new Node(value);
            //把新结点的右子树设置为当前结点的右子树
            newNode.right = this.right;
            //把新结点的左子树设置为当前结点的左子树的右子树
            newNode.left = this.left.right;
            //把当前结点的值替换为左子结点的值
            this.value = this.left.value;
            //把当前结点的左子树设置为当前结点的左子树的左子树
            this.left = this.left.left;
            //把当前结点的右子树设置为新的结点
            this.right = newNode;
        }

        //返回左子树的高度
        public int leftHeight() {
            if (left == null)
                return 0;
            else {
                return left.height();
            }
        }

        //返回右子树的高度
        public int rightHeight() {
            if (right == null)
                return 0;
            else
                return right.height();
        }

        //返回当前结点的高度,以该结点为根结点树的高度
        public int height() {
            return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
        }

        //查找需要删除的结点
        //value->希望删除结点的值,如果找到返回该结点,否则返回null
        public Node searchTarget(int value) {
            if (value == this.value)//此时找到希望删除的结点
                return this;
            else if (value < this.value) {//如果查找的值小于当前结点,向左子树递归查找
                if (this.left == null)//此时左子结点为空,找不到
                    return null;
                else
                    return this.left.searchTarget(value);
            } else//此时查找的值大于当前结点,向右子树递归查找
            {
                if (this.right == null)//此时右子树为空,找不到
                    return null;
                else
                    return this.right.searchTarget(value);
            }
        }

        //查找要删除结点的父结点(parentNode)
        //value->要找到结点的值
        public Node searchParentNode(int value) {
            if ((this.left != null && this.left.value == value) ||
                    (this.right != null && this.right.value == value)) {
                //此时this就位parentNode
                return this;
            } else {
                //如果查找的值比当前的值要小,并且当前的左子结点不为空,则向左子树递归查找parentNode
                if (value < this.value && this.left != null)
                    return this.left.searchParentNode(value);
                    //如果查找的值比当前的值要大,并且当前的右子结点不为空,则向右子树递归查找parentNode
                else if (value >= this.value && this.right != null)
                    return this.right.searchParentNode(value);
                else {//此时上述条件都不满足,即为没有父节点
                    return null;
                }
            }
        }


        //添加结点的方法
        //以递归的形式添加结点,需要满足二叉排序树的要求
        public void addNode(Node node) {
            if (node == null)//此时没有添加内容
                return;

            //判断传入的值与当前子树根结点的值之间大小关系
            if (node.value < this.value)//此时传入的值小于根结点的值,应放入根节点的左边
            {
                if (this.left == null)//判断根结点的左子结点是否为空
                    this.left = node;
                else
                    this.left.addNode(node);//如果不为空,递归向左子树添加
            } else //此时传入的值大于根结点的值,应放入根节点的右边
            {
                if (this.right == null)//判断根结点的右子结点是否为空
                    this.right = node;
                else
                    this.right.addNode(node);//递归向右子结点添加
            }
            //当右子树高度-左子树高度大于1时,左旋转
            if(rightHeight() - leftHeight() > 1)
            {
                //如果它的右子树的左子树高度大于它的右子树高度
                if (this.right != null && this.right.leftHeight() > this.right.rightHeight()) {
                    this.right.rightRotate();//对右子结点进行右反转
                    //再对当前结点进行左旋转
                    leftRotate();
                }else
                {
                    //直接左旋转
                    leftRotate();
                }
                return;
            }
            //当左子树高度-右子树高度大于1时,右旋转
            if (leftHeight() - rightHeight() > 1) { //如果
                //如果它的左子树的右子树高度大于它的左子树高度
                if(this.left != null && this.left.rightHeight() > this.left.leftHeight())
                {
                    //先对当前结点的左结点进行左旋转
                    this.left.leftRotate();
                    //再对当前结点进行右旋转
                    rightRotate();
                }else {
                    //直接进行右旋转
                    rightRotate();
                }

                return;
            }
        }

        //中序遍历
        public void infixOrder() {
            if (this.left != null)
                this.left.infixOrder();
            System.out.println(this);
            if (this.right != null)
                this.right.infixOrder();
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public Node getLeft() {
            return left;
        }

        public void setLeft(Node left) {
            this.left = left;
        }

        public Node getRight() {
            return right;
        }

        public void setRight(Node right) {
            this.right = right;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "value=" + value +
                    '}';
        }
    }
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值