平衡二叉树与java实现

本文详细介绍了平衡二叉树的概念,包括其定义、结点的平衡因子及其计算,以及查询、删除操作。重点讲解了四种平衡操作:单向左旋、单向右旋、双向旋转(先左后右)、双向旋转(先右后左)。同时,提供了一个完整的Java代码实现,包括插入、删除和平衡调整等方法,展示了如何在实际编程中应用平衡二叉树。
摘要由CSDN通过智能技术生成

平衡二叉树

定义

亦称为AVL

空树和1个点节点的树都是平衡二叉树

平衡二叉树是在二叉排序树上的扩展延伸,除了自身的性质还满足以下性质的二叉树:

  • 树中的子树都是平衡二叉树
  • 每颗子树的左子树和右子树的深度之差的绝对值不超过1。

结点的平衡因子BF(balance factor)

该结点的左子树的深度减去右子树的深度,则平衡二叉树上所有节点的平衡因子的值为-1,0,1。

查询,删除过程

它的查询和删除系列操作思路跟二叉排序树差不多,可以阅读二叉排序树的JAVA实现

检查平衡

通过计算每个结点的平衡因子来判断是否平衡。只要平衡因子为2 或 -2,就说明此结点子树不平衡。

平衡操作–左旋和右旋

当平衡二叉树由于新增数据元素导致整棵树的平衡遭到破坏时,就需要根据实际情况做出适当的调整,寻找距离插入结点最近的“不平衡因子”。则调整的规律可归纳为以下 4 种情况:

  • 单向右旋平衡处理:若由于结点A的左子树为根结点的左子树上插入结点,导致结点A的平衡因子由 1 增至 2,致使以A为根结点的子树失去平衡,则只需进行一次向右的顺时针旋转,如下图这种情况:
    在这里插入图片描述

  • 单向左旋平衡处理:如果由于结点A的右子树为根结点的右子树上插入结点,导致结点A的平衡因子由 -1变为 -2,则以A为根结点的子树需要进行一次向左的逆时针旋转,如下图这种情况:
    在这里插入图片描述

  • 双向旋转(先左后右)平衡处理:如果由于结点A的左子树为根结点的右子树上插入结点,导致结点A衡因子由 1 增至 2,致使以A为根结点的子树失去平衡,则需要进行两次旋转操作,先以A的左子结点B为根结点进行左旋,在以A为根结点进行右旋,如下图这种情况:
    在这里插入图片描述

  • 双向旋转(先右后左)平衡处理:如果由于结点 A 的右子树为根结点的左子树上插入结点,导致结点 A 平衡因子由 -1 变为 -2,致使以 a 为根结点的子树失去平衡,则需要进行两次旋转操作,先以A的左子结点B为根结点进行右旋,在以A为根结点进行左旋,如下图这种情况:
    在这里插入图片描述

JAVA代码实现

package tree.avl;

import org.apache.commons.lang.StringUtils;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @author Donny
 * @date 2022/5/18
 */
public class AvlTree {

    public static final StringBuilder VALUES = new StringBuilder();
    public static final String SEPARATOR = "->";
    public static final int SEPARATOR_LENGTH = SEPARATOR.length();

    /**
     * 右子树需要平衡操作
     */
    private final int RIGHT = 0;
    
    /**
     * 左子树需要平衡操作
     */
    private final int LEFT = 1;
    
    /**
     * 树根
     */
    private AvlTreeNode root;

    /**
     * 树的结点类
     */
    private static class AvlTreeNode {
        /**
         * 存储的数值
         */
        private int data;
        /**
         * 左子结点
         */
        private AvlTreeNode leftChild;
        /**
         * 右子结点
         */
        private AvlTreeNode rightChild;
        /**
         * 双亲结点
         */
        private AvlTreeNode parentNode;

        public AvlTreeNode(int data) {
            this(data, null, null, null);
        }

        public AvlTreeNode(int data, AvlTreeNode parentAvlTreeNode) {
            this(data, null, null, parentAvlTreeNode);
        }

        public AvlTreeNode(int data, AvlTreeNode leftAvlTreeNode, AvlTreeNode rightAvlTreeNode, AvlTreeNode parentAvlTreeNode) {
            this.data = data;
            this.leftChild = leftAvlTreeNode;
            this.rightChild = rightAvlTreeNode;
            this.parentNode = parentAvlTreeNode;
        }

        /**
         * 计算结点的高度
         */
        public int height() {
            return Math.max(leftChild == null ? 0 : leftChild.height(), rightChild == null ? 0 : rightChild.height()) + 1;
        }

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

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

        /**
         * 计算平衡因子
         */
        public int calcBalanceFactor() {
            return leftHeight() - rightHeight();
        }
    }

    /**
     * 以node为根结点 左旋
     */
    public AvlTreeNode leftRotation(AvlTreeNode node) {

        if (node != null) {
            // 将当前结点的双亲结点转移给当前结点的右子结点
            AvlTreeNode rightChild = node.rightChild;
            // 右子结点的左子结点作为当前结点的右结点
            node.rightChild = rightChild.leftChild;
            if (rightChild.leftChild != null) {
                rightChild.leftChild.parentNode = node;
            }
            // 右子结点的双亲结点变为当前结点的双亲结点
            rightChild.parentNode = node.parentNode;
            // 判断当前结点的父结点是否存在
            if (node.parentNode == null) {
                this.root = rightChild;
            }
            /*
             * 当前结点结点不是根结点 分两种情况
             *   1、当前结点结点位于其父结点左边,则原左子结点也要位于左边
             *   2、当前结点结点位于其父结点右边,则原左子结点也要位于右边
             */
            else if (node.parentNode.rightChild == node) {
                node.parentNode.rightChild = rightChild;
            } else if (node.parentNode.leftChild == node) {
                node.parentNode.leftChild = rightChild;
            }
            // 将右子结点的左子结点改为当前结点
            rightChild.leftChild = node;
            // 将原右子结点当前结点的双亲结点
            node.parentNode = rightChild;

        }
        return null;
    }

    /**
     * 以node为根结点 右旋
     */
    public AvlTreeNode rightRotation(AvlTreeNode node) {
        if (node != null) {
            // 用变量存储node结点的左子结点
            AvlTreeNode leftChild = node.leftChild;
            // 将leftChild结点的右子结点赋值给node结点的左结点
            node.leftChild = leftChild.rightChild;
            // 如果leftChild的右结点存在,则需将该右结点的父结点指给node结点
            if (leftChild.rightChild != null) {
                leftChild.rightChild.parentNode = node;
            }

            // 将当前结点的双亲结点转移给当前结点的左子结点
            leftChild.parentNode = node.parentNode;
            if (node.parentNode == null) {
                // 即表明node结点为根结点
                this.root = leftChild;
            }
            /*
             * 当前结点结点不是根结点 分两种情况
             *   1、当前结点结点位于其父结点左边,则原左子结点也要位于左边
             *   2、当前结点结点位于其父结点右边,则原左子结点也要位于右边
             */
            else if (node.parentNode.rightChild == node) {
                // 即node结点在它原父结点的右子树中
                node.parentNode.rightChild = leftChild;
            } else if (node.parentNode.leftChild == node) {
                node.parentNode.leftChild = leftChild;
            }
            leftChild.rightChild = node;
            node.parentNode = leftChild;
            return leftChild;
        }
        return null;
    }

    /**
     * 新增key
     */
    public void insert(int key) {
        // 若树根为null 则新建结点作为树根
        if (null == root) {
            root = new AvlTreeNode(key);
            return;
        }

        // p作为遍历树结点的临时帮助结点
        AvlTreeNode p = root;
        // prev代表新增key的结点的双亲结点
        AvlTreeNode prev = null;
        while (null != p) {
            prev = p;
            if (key > p.data) {
                p = p.rightChild;
            } else if (key < p.data) {
                p = p.leftChild;
            } else {
                return;
            }
        }

        // 此时找到了key对应的双亲结点prev
        AvlTreeNode node = new AvlTreeNode(key, prev);
        if (key > prev.data) {
            // 新增结点是prev的右子结点
            prev.rightChild = node;
        } else {
            // 新增结点是prev的左子结点
            prev.leftChild = node;
        }

        rebuild(prev);
    }

    /**
     * 计算结点的平衡因子进行平衡操作
     * 平衡因子为-1,0,1是为平衡的,所有一旦平衡因子为2或-2就需要进行平衡操作。
     */
    private void rebuild(AvlTreeNode p) {
        while (p != null) {
            if (p.calcBalanceFactor() == 2) {
                // 说明左子树高,需要【右旋】或者【先左旋后右旋】
                fixAfterInsertion(p, LEFT);
            } else if (p.calcBalanceFactor() == -2) {
                // 说明右子树高,需要【左旋】或者【先右旋后左旋】
                fixAfterInsertion(p, RIGHT);
            }
            p = p.parentNode;
        }
    }

    private void fixAfterInsertion(AvlTreeNode p, int type) {
        // 左子树失衡
        if (type == LEFT) {
            final AvlTreeNode leftChild = p.leftChild;
            // 左子结点不为空,直接右旋
            if (leftChild.leftChild != null) {
                //LL型
                rightRotation(p);
            } else if (leftChild.rightChild != null) {
                // 先左旋后右旋 LR型
                leftRotation(leftChild);
                rightRotation(p);
            }
        } else {
            // 右子树失衡
            final AvlTreeNode rightChild = p.rightChild;
            // 右子结点不为空,直接左旋
            if (rightChild.rightChild != null) {
                // 左旋 RR型
                leftRotation(p);
            } else if (rightChild.leftChild != null) {
                // 先右旋,后左旋 RL型
                rightRotation(p);
                leftRotation(rightChild);
            }
        }
    }

    /**
     * 中序输出
     */
    public String inOrderPrint() {
        return inOrderPrint(this.root);
    }

    /**
     * 中序输出
     */
    private String inOrderPrint(AvlTreeNode node) {
        String result = "";
        if (node != null) {
            inOrderPrint(node.leftChild);
            VALUES.append(node.data).append(SEPARATOR);
            inOrderPrint(node.rightChild);
        }
        if (StringUtils.isNotBlank(VALUES.toString())) {
            result = VALUES.substring(0, VALUES.toString().length() - SEPARATOR_LENGTH);
        }
        return result;
    }

    public void printLeft() {
        if (this.root == null) {
            return;
        }
        Queue<AvlTreeNode> queue = new LinkedList<>();
        AvlTreeNode temp;
        queue.add(root);
        while (!queue.isEmpty()) {
            temp = queue.poll();
            System.out.print("结点值:" + temp.data + ",平衡值:" + temp.calcBalanceFactor() + "\n");
            if (temp.leftChild != null) {
                queue.add(temp.leftChild);
            }
            if (temp.rightChild != null) {
                queue.add(temp.rightChild);
            }
        }
    }

    public AvlTreeNode getNode(int value) {
        AvlTreeNode temp = root;
        int t;
        do {
            t = temp.data - value;
            if (t > 0) {
                temp = temp.leftChild;
            } else if (t < 0) {
                temp = temp.rightChild;
            } else {
                return temp;
            }
        } while (temp != null);
        return null;
    }

    /**
     * 找到node结点的后继结点
     * 1、先判断该结点有没有右子树,如果有,则从右结点的左子树中寻找后继结点,没有则进行下一步
     * 2、查找该结点的父结点,若该父结点的右结点等于该结点,则继续寻找父结点,
     *   直至父结点为Null或找到不等于该结点的右结点。
     *   理由,后继结点一定比该结点大,若存在右子树,则后继结点一定存在右子树中,这是第一步的理由
     *   若不存在右子树,则也可能存在该结点的某个祖父结点(即该结点的父结点,或更上层父结点)的右子树中,
     *   对其迭代查找,若有,则返回该结点,没有则返回null
     */
    private AvlTreeNode getSuccessor(AvlTreeNode node) {
        if (node.rightChild != null) {
            AvlTreeNode rightChild = node.rightChild;
            while (rightChild.leftChild != null) {
                rightChild = rightChild.leftChild;
            }
            return rightChild;
        }
        AvlTreeNode parent = node.parentNode;
        while (parent != null && (node == parent.rightChild)) {
            node = parent;
            parent = parent.parentNode;
        }
        return parent;
    }

    public void delete(int key) {
        delete(root, key);
    }

    private boolean delete(AvlTreeNode treeNode, int key) {
        if (treeNode == null) {
            return false;
        } else {
            if (key == treeNode.data) {
                return delete(treeNode);
            } else if (key < treeNode.data) {
                return delete(treeNode.leftChild, key);
            } else {
                return delete(treeNode.rightChild, key);
            }
        }
    }

    public boolean delete(AvlTreeNode node) {
        if (node == null) {
            return false;
        }
        // 被删除结点的双亲结点
        AvlTreeNode p;
        AvlTreeNode parent = node.parentNode;
        AvlTreeNode leftChild = node.leftChild;
        AvlTreeNode rightChild = node.rightChild;

        if (leftChild == null && rightChild == null) {
            //没有子结点
            if (parent != null) {
                if (parent.leftChild == node) {
                    parent.leftChild = null;
                } else if (parent.rightChild == node) {
                    parent.rightChild = null;
                }
            } else {
                //不存在父结点,则表明删除结点为根结点
                root = null;
            }
            p = parent;
            node = null;
        }
        // 只有右子结点
        else if (leftChild == null) {
            if (parent != null && parent.data > node.data) {
                // 存在父结点,且node位置为父结点的左边
                parent.leftChild = rightChild;
            } else if (parent != null && parent.data < node.data) {
                // 存在父结点,且node位置为父结点的右边
                parent.rightChild = rightChild;
            } else {
                root = rightChild;
            }
            p = parent;
            node = null;
        }
        // 只有左子结点
        else if (rightChild == null) {
            if (parent != null && parent.data > node.data) {
                // 存在父结点,且node位置为父结点的左边
                parent.leftChild = leftChild;
            } else if (parent != null && parent.data < node.data) {
                // 存在父结点,且node位置为父结点的右边
                parent.rightChild = leftChild;
            } else {
                root = leftChild;
            }
            p = parent;
            node = null;
        }
        // 两个子结点都存在
        else {
            // 这种情况,一定存在后继结点或前驱结点
            AvlTreeNode successor = getSuccessor(node);
            int temp = successor.data;
            boolean delete = delete(successor);
            if (delete) {
                node.data = temp;
            }
            p = successor;
            successor = null;
            node = null;
        }
        rebuild(p);
        return true;
    }


    public static void main(String[] args) {
        AvlTree avlTree = new AvlTree();
        int[] ints = {10, 9, 11, 7, 12, 8, 40, 35, 16, 4, 3};
        for (int i : ints) {
            avlTree.insert(i);
        }

        avlTree.printLeft();
        System.out.println(avlTree.inOrderPrint());
        VALUES.delete(0, VALUES.length());
        
        avlTree.delete(9);
        avlTree.printLeft();
        System.out.println(avlTree.inOrderPrint());
    }
}

参考:

https://www.cnblogs.com/qm-article/p/9349681.html

http://c.biancheng.net/view/3432.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

顧棟

若对你有帮助,望对作者鼓励一下

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

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

打赏作者

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

抵扣说明:

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

余额充值