【数据结构与算法】平衡二叉树(AVL树)

这篇具有很好参考价值的文章主要介绍了【数据结构与算法】平衡二叉树(AVL树)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

平衡二叉树(AVL树)

给你一个数列{1,2,3,4,5,6},要求创建二叉排序树(BST),并分析问题所在。

【数据结构与算法】平衡二叉树(AVL树),数据结构和算法,算法,java,数据结构

BST 存在的问题分析

  1. 左子树全部为空,从形式上看,更像一个单链表。
  2. 插入速度没有影响。
  3. 查询速度明显降低(因为需要依次比较),不能发挥 BST 的优势,因为每次还需要比较左子树,其查询速度比单链表还慢。
  4. 解决方案:平衡二叉树(AVL)

基本介绍

  1. 平衡二叉树也叫平衡二叉搜索树(Self - balancing binary search tree)又被称为 AVL 树,可以保证查询速率较高
  2. 具有一下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不能超过 1,并且左右两个子树都是一棵平衡二叉树,平衡二叉树的常用实现方法有:红黑树、AVL、替罪羊树、Treap、伸展树等。

应用案例 - 左旋转

给你一个数列{4,3,6,5,7,8},构建成一棵平衡二叉树。

问题:

当插入 8 时,rightHeight() - leftHight() > 1 成立,此时,不再是一棵 AVL 树了。

思路 - 左旋转:

  1. 创建一个新的节点 newNode(以 4 这个值创建),值等于当前根节点的值;
  2. 把新节点的左子树设置为当前节点的左子树;
  3. 把新节点的右子树设置为当前节点右子树的左子树;
  4. 把当前节点的值换为右子节点的值;
  5. 把当前节点的右子树设置为右子树的右子树;
  6. 把当前节点的左子树设置为新节点。
    【数据结构与算法】平衡二叉树(AVL树),数据结构和算法,算法,java,数据结构

代码实现:文章来源地址https://www.toymoban.com/news/detail-635082.html

public class AVLTreeDemo {
    public static void main(String[] args) {
        int[] arr = {4, 3, 6, 5, 7, 8};
        // 创建一个 AVLTree
        AVLTree avlTree = new AVLTree();
        // 添加节点
        for (int i = 0; i < arr.length; i++) {
            avlTree.add(new Node(arr[i]));
        }

        // 遍历
        System.out.println("中序遍历");
        avlTree.infixOrder();
        System.out.println("左子树:" + avlTree.getRoot().leftHeight());
        System.out.println("右子树:" + avlTree.getRoot().rightHeight());
        System.out.println("树:" + avlTree.getRoot().height());
    }
}

// 创建 AVLTree
class AVLTree {
    private Node root;

    /**
     * 查找要删除的节点
     *
     * @param value 要删除的节点的值
     * @return 如果找到,返回节点,否则,返回 null
     */
    public Node search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    /**
     * 得到以 node 为节点的最小节点的值,并删除该值
     *
     * @param node 传入的节点
     * @return 返回的是以 node 为根节点的二叉排序树的最小节点的值
     */
    public int delRightTreeMin(Node node) {
        Node target = node;
        // 循环查找左节点,就会找到最小值
        while (target.left != null) {
            target = target.left;
        }
        // 这时 target 就指向了最小节点
        // 删除最小节点
        delNode(target.value);
        return target.value;
    }

    /**
     * 得到以 node 为节点的最大节点的值,并删除该值
     *
     * @param node 传入的节点
     * @return 返回的是以 node 为根节点的二叉排序树的最大节点的值
     */
    public int delLiftTreeMax(Node node) {
        Node target = node;
        // 循环查找右节点,就会找到最小值
        while (target.right != null) {
            target = target.right;
        }
        // 这时 target 就指向了最大节点
        // 删除最大节点
        delNode(target.value);
        return target.value;
    }

    /**
     * 删除节点
     *
     * @param value 要删除节点的值
     */
    public void delNode(int value) {
        if (root == null) {
            return;
        } else {
            // 1. 需要先去找到要删除的节点
            Node targetNode = search(value);
            // 如果没有找到要删除的节点
            if (targetNode == null) {
                return;
            }
            // 如果我们发现当前这棵二叉排序树只有一个节点
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }
            // 去找到 targetNode 的父节点
            Node parent = searchParent(value);
            // 第一种情况
            // 如果要删除节点是叶子结点
            if (targetNode.left == null && targetNode.right == null) {
                // 判断 targetNode 是父节点的左子节点还是右子节点
                if (parent.left != null && parent.left.value == value) { // 是左子节点
                    parent.left = null;
                } else if (parent.right != null && parent.right.value == value) { // 是右子节点
                    parent.right = null;
                }
            } else if (targetNode.left != null && targetNode.right != null) {
                // 第三种情况
                // 如果要删除的节点是有两棵子树的节点
                // 向右子树找最小值
//                targetNode.value = delRightTreeMin(targetNode.right);
                // 向左子树找最大值
                targetNode.value = delLiftTreeMax(targetNode.left);
            } else {
                // 第二种情况
                // 如果要删除的节点是只有一棵子树的的节点
                // 如果要删除的节点有左子节点
                if (targetNode.left != null) {
                    if (parent != null) {
                        // 如果 targetNode 是 parent 的左子节点
                        if (parent.left.value == value) {
                            parent.left = targetNode.left;
                        } else { // 如果 targetNode 是 parent 的右子节点
                            parent.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else { // 如果要删除的节点有右子节点
                    if (parent != null) {
                        // 如果 targetNode 是 parent 的左子节点
                        if (parent.left.value == value) {
                            parent.left = targetNode.right;
                        } else { // 如果 targetNode 是 parent 的右子节点
                            parent.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }
                }
            }
        }
    }

    /**
     * 查找要删除节点的父节点
     *
     * @param value 要删除节点的值
     * @return 如果找到,放回父节点,否则,返回 null
     */
    public Node searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    /**
     * 添加节点的方法
     *
     * @param node 需要添加的节点
     */
    public void add(Node node) {
        // 如果 root 为空,则直接让 root 指向 node
        if (root == null) {
            root = node;
        } else {
            root.add(node);
        }
    }

    /**
     * 中序遍历
     */
    public void infixOrder() {
        if (root != null) {
            root.infixOrder();
        } else {
            System.out.println("二叉排序树为空,不能遍历");
        }
    }

    public Node getRoot() {
        return root;
    }

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

// 创建 Node 节点
class Node {
    int value;
    Node left;
    Node right;

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

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

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

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

    /**
     * 左旋转
     */
    private void leftRotate() {
        // 1. 创建一个新的节点 newNode(以 4 这个值创建),值等于当前根节点的值;
        Node newNode = new Node(value);
        // 2. 把新节点的左子树设置为当前节点的左子树;
        newNode.left = left;
        // 3. 把新节点的右子树设置为当前节点右子树的左子树;
        newNode.right = right.left;
        // 4. 把当前节点的值换为右子节点的值;
        value = right.value;
        // 5. 把当前节点的右子树设置为右子树的右子树;
        right = right.right;
        // 6. 把当前节点的左子树设置为新节点。
        left = newNode;
    }

    /**
     * 查找要删除的节点
     *
     * @param value 希望删除的节点的值
     * @return 如果找到返回该节点,否则,返回 null
     */
    public Node search(int value) {
        if (value == this.value) { // 找到该节点
            return this;
        } else if (value < this.value) { // 如果查找的节点小于当前节点,向左子树递归查找
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        } else { // 如果查找的节点大于或等于当前节点,向右子树递归查找
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }

    /**
     * 查找要删除节点的父节点
     *
     * @param value 要找到的节点的值
     * @return 返回的是要删除节点的父节点,如果没有就返回 null
     */
    public Node searchParent(int value) {
        if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
            return this;
        } else {
            // 如果查找的这个值小于当前节点的值,并且当前节点的左子节点不为空
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value); // 向左子树递归查找
            } else if (value >= this.value && this.right != null) { // 如果查找的这个值大于等于当前节点的值,并且当前节点的右子节点不为空
                return this.right.searchParent(value); // 向右子树递归查找
            } else {
                return null;
            }
        }
    }

    /**
     * 添加节点的方法
     * 通过递归的方式添加节点,注意需要满足二叉排序树的要求
     *
     * @param node 需要添加的节点
     */
    public void add(Node node) {
        if (node == null) {
            return;
        }
        // 判断出入节点的值和当前子树的根节点的关系
        if (node.value < this.value) {
            // 如果当前节点的左子节点为 null
            if (this.left == null) {
                this.left = node;
            } else {
                // 递归向左子树添加
                this.left.add(node);
            }
        } else {
            // 如果当前节点的右子节点为 null
            if (this.right == null) {
                this.right = node;
            } else {
                // 递归向右子树添加
                this.right.add(node);
            }
        }
        // 当添加完一个节点后,如果 右子树的高度 - 左子树的高度 > 1,左旋转
        if (rightHeight() - leftHeight() > 1) {
            leftRotate(); // 左旋转
        }
    }

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

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }
}

应用案例 - 右旋转

给你一个数列{10,12,8,9,7,6},构建成一棵平衡二叉树。

问题:

当插入 6 时,leftHight() - rightHeight() > 1 成立,此时,不再是一棵 AVL 树了。

思路 - 左旋转:

  1. 创建一个新的节点 newNode(以 10 这个值创建),值等于当前根节点的值;
  2. 把新节点的右子树设置为当前节点的右子树;
  3. 把新节点的左子树设置为当前节点左子树的右子树;
  4. 把当前节点的值换为左子节点的值;
  5. 把当前节点的左子树设置为左子树的左子树;
  6. 把当前节点的右子树设置为新节点。

【数据结构与算法】平衡二叉树(AVL树),数据结构和算法,算法,java,数据结构

代码实现:

/**
 * 右旋转
 */
private void rightRotate() {
    // 1. 创建一个新的节点 newNode(以 10 这个值创建),值等于当前根节点的值;
    Node newNode = new Node(value);
    // 2. 把新节点的右子树设置为当前节点的右子树;
    newNode.right = right;
    // 3. 把新节点的左子树设置为当前节点左子树的右子树;
    newNode.left = left.right;
    // 4. 把当前节点的值换为左子节点的值;
    value = left.value;
    // 5. 把当前节点的左子树设置为左子树的左子树;
    left = left.left;
    // 6. 把当前节点的右子树设置为新节点。
    right = newNode;
}

应用案例 - 双旋转

给你一个数列{10,11,7,6,8,9},构建成一棵平衡二叉树。

问题:

当插入 9 时,leftHight() - rightHeight() > 1 成立,此时,不再是一棵 AVL 树了。但是,当进行了右旋转后发现,它依旧不是一棵 AVL 树。

【数据结构与算法】平衡二叉树(AVL树),数据结构和算法,算法,java,数据结构

思路:

  1. 当符合右旋转条件时
  2. 如果它的左子树的右子树高度大于它的左子树的左子树的高度
  3. 先对当前这个节点的左节点进行左旋转
  4. 再对当前节点进行右旋转的操作即可

【数据结构与算法】平衡二叉树(AVL树),数据结构和算法,算法,java,数据结构

代码实现:

/**
 * 添加节点的方法
 * 通过递归的方式添加节点,注意需要满足二叉排序树的要求
 *
 * @param node 需要添加的节点
 */
public void add(Node node) {
    if (node == null) {
        return;
    }
    // 判断出入节点的值和当前子树的根节点的关系
    if (node.value < this.value) {
        // 如果当前节点的左子节点为 null
        if (this.left == null) {
            this.left = node;
        } else {
            // 递归向左子树添加
            this.left.add(node);
        }
    } else {
        // 如果当前节点的右子节点为 null
        if (this.right == null) {
            this.right = node;
        } else {
            // 递归向右子树添加
            this.right.add(node);
        }
    }
    // 当添加完一个节点后,如果 右子树的高度 - 左子树的高度 > 1,左旋转
    if (rightHeight() - leftHeight() > 1) {
        // 如果它的右子树的左子树高度大于它的右子树的右子树的高度
        if (right != null && right.leftHeight() > right.rightHeight()) {
            // 先对当前这个节点的右节点进行右旋转
            right.rightRotate();
            // 再对当前节点进行左旋转的操作即可
            leftRotate();
        } else {
            // 直接进行左旋转
            leftRotate();
        }
        return;
    }
    // 当添加完一个节点后,如果 左子树的高度 - 右子树的高度 > 1,右旋转
    if (leftHeight() - rightHeight() > 1) {
        // 如果它的左子树的右子树高度大于它的左子树的左子树的高度
        if (left != null && left.rightHeight() > left.leftHeight()) {
            // 先对当前这个节点的左节点进行左旋转
            left.leftRotate();
            // 再对当前节点进行右旋转的操作即可
            rightRotate();
        } else {
            // 直接进行右旋转
            rightRotate();
        }
    }
}

到了这里,关于【数据结构与算法】平衡二叉树(AVL树)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java学数据结构(2)——树Tree & 二叉树binary tree & 二叉查找树 & AVL树 & 树的遍历

    1.树的出现:解决链表线性访问时间太慢,树的时间复杂度O(logN); 2.二叉树的定义,最多两个儿子节点; 3.二叉查找树,左小,右大,中居中;remove方法,两种,只有一个儿子节点,有两个儿子节点; 4.AVL树,在二叉查找树基础上加平衡条件,旋转方法,单旋转,双旋转;

    2024年02月10日
    浏览(49)
  • 数据结构奇妙旅程之二叉平衡树进阶---AVL树

    ꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶ 个人主页:xiaoxieʕ̯

    2024年03月13日
    浏览(82)
  • 数据结构07:查找[C++][平衡二叉排序树AVL]

    图源:文心一言 考研笔记整理1w+字,小白友好、代码可跑,请小伙伴放心食用~~🥝🥝 第1版:查资料、写BUG、画导图、画配图~🧩🧩 参考用书: 王道考研《2024年 数据结构考研复习指导》 参考用书配套视频: 7.3_2 平衡二叉树_哔哩哔哩_bilibili 特别感谢:  Chat GPT老师、文心

    2024年02月11日
    浏览(47)
  • 数据结构:搜索二叉树 | 平衡二叉树

    博客写的代码都放在这里:gitee仓库链接 1.二叉搜索树 1.1.基本概念 二叉搜索树又称二叉排序树, 可以为空,如果不为空具有以下性质的二叉树 : 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,则右子树上所有节点的值都大于根节点的

    2024年01月23日
    浏览(54)
  • 【数据结构】平衡二叉搜索树(AVL树)——AVL树的概念和介绍、AVL树的简单实现、AVL树的增删查改

      为什么要引入平衡二叉搜索树?   在之前我们学习了二叉搜索树,二叉搜索树的结构类似于一个倒置的树,而左子树的值小于根节点的值,右节点的值大于根节点的值,这种结构使得二叉搜索树在处理有序数据时非常高效。但是如果 在传入的数据为有序或接近有序,

    2024年02月07日
    浏览(47)
  • 数据结构-----平衡二叉树

      目录 前言 1.平衡二叉树 1.1概念与特点 1.2与二叉排序树比较 1.3判断平衡二叉树 2.平衡二叉树的构建 2.1平衡因子 BF 2.2 LL型失衡(右旋) 2.3 RR型失衡(左旋) 2.4 LR型失衡(先左旋再右旋)  2.5 RL型失衡(先右旋再左旋) 2.6构建平衡二叉树代码实现  3.删除节点操作 3.1删除叶

    2024年04月13日
    浏览(32)
  • 【数据结构】平衡二叉树

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、平衡二叉树的介绍 二、平衡二叉树的插入 1、平衡二叉树的插入步骤 2、平衡二叉树的旋转 2.1左单旋 2.2右单旋 2

    2024年02月03日
    浏览(84)
  • 数据结构之二叉树和平衡二叉树

    1、二叉树: 2、平衡二叉树:

    2024年04月17日
    浏览(44)
  • 数据结构之平衡二叉树的平衡调整

    1:LL型调整 2:RR型调整 3:LR型调整 4:RL型调整 5:总结 作者约定:将导致不平衡的结点称作 被破坏者 ,破坏了结点的平衡的结点成为 破坏者 ,经过调整可以让该树平衡的结点称为 调整结点 。 LL型不平衡调整方法:以调整结点为中心,进行右旋操作,就可以使树平衡。

    2024年02月09日
    浏览(44)
  • 数据结构之平衡二叉树详解

    平衡二叉树(balanced binary tree) 又称AVL树(Adelson-Velskii and Landis) 一棵平衡二叉树或者是空树,或者是具有下列性质的 二叉排序树 :         1,左子树与右子树的高度之差的绝对值小于等于1;         2,左子树和右子树也是平衡二叉排序树. 为了方便起见,给每

    2024年02月03日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包