计算机基础--->数据结构(6)【AVL树(平衡二叉树)】

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

AVL(平衡二叉树)树

平衡二叉树是一种特殊的二叉搜索树,他的左子树与右子树的高度差不超过1。并且左右两个子树都是一颗平衡二叉树。保持平衡的特性使得平衡二叉树的查询、插入和删除操作在平均和最坏情况下的时间复杂度都是O(logn),其中n是树中节点的数量。 相比于普通的二叉搜索树,平衡二叉树更加适合处理动态变化的数据集。 常见的平衡二叉树有AVL树、红黑树以及B树等。这些树结构通过在插入或删除节点时进行特定的平衡操作来保持树的平衡。

性质

  • 它的左右子树都是AVL树
  • 左右子树高度差不超过1
  • 完全二叉树是AVL树,如果一棵树是AVL树,那么其高度可保持在O(log2n),搜索时间复杂度为O(log2n)

AVL树的操作(Java)

首先创建节点用来保存树中的节点的数据,其次和二叉搜索树不同的是,AVL树需要平衡因子来控制二叉树的平衡。

节点的创建

    private class Node {
        int val;
        Node left;
        Node right;
        int height;

        public Node(int val) {
            this.val = val;
            this.left = this.right = null;
            this.height = 1;
        }
    }

AVL树的插入

插入操作和二叉搜索树是相同的,但是在插入结束时会判断AVL树是否平衡。

    // 向树中添加节点
    public void add(int val) {
        if (contains(val)) {
            return;
        }
        this.root = add(this.root, val);
        this.size++;
    }

    // 向AVL树中添加节点
    private Node add(Node node, int val) {
        // 递归到底的情况
        if (node == null) {
            return new Node(val);
        }
        if (node.val > val) {
            node.left = add(node.left, val);
        } else {
            node.right = add(node.right, val);
        }
        // 更新节点高度
        node.height = Math.max(getNodeHeight(node.left), getNodeHeight(node.right)) + 1;

        // 维护平衡
        int balance = getBalance(node);
        if (balance > 1 && getBalance(node.left) >= 0) {
            // 右旋
            return rightRotate(node);
        } else if (balance > 1 && getBalance(node.left) <= 0) {
            // 左旋右旋
            node.left = leftRotate(node.left);
            return rightRotate(node);
        } else if (balance < -1 && getBalance(node.right) >= 0) {
            // 右旋左旋
            node.right = rightRotate(node.right);
            return leftRotate(node);
        } else if (balance < -1 && getBalance(node.right) <= 0) {
            // 左旋
            return leftRotate(node);
        }
        return node;
    }

1.判断平衡

一个二叉树是否是平衡的,根绝二叉树的性质可以知道,当其左右子树节点的高度差的绝对值不超过1时,这个二叉树就是平衡二叉树,当其高度差绝对值超过1时,那么就是不平衡的二叉树,就需要对二叉树进行平衡调整。

 // 获取当前节点的平衡因子(左右子树高度差)
    private int getBalance(Node node) {
        if (node == null) {
            return 0;
        }
        return getNodeHeight(node.left) - getNodeHeight(node.right);
    }

2.保持树的平衡

当判断出一棵树不平衡时,需要进行平衡调整。

二叉树一般出现不平衡的时候有四种状态:

1. 左旋
计算机基础--->数据结构(6)【AVL树(平衡二叉树)】,计算机基础,# 数据结构,数据结构,AVL树,二叉树

根据上图,我们可以直到,当二叉树的右树的高度超过平衡时需要进行左旋,左旋的方式是先将x节点的左子树单独定义出来为t2,然后将y节点连接到x的左子树,将t2连接到y的右子树上,这时就完成了左旋。

    // 左旋转
    private Node leftRotate(Node y) {
        Node x = y.right;
        Node leftX = x.left;
        x.left = y;
        y.right = leftX;
        y.height = 1 + Math.max(getNodeHeight(y.left), getNodeHeight(y.right));
        x.height = 1 + Math.max(getNodeHeight(x.left), getNodeHeight(x.right));
        return x;
    }

2. 右旋
计算机基础--->数据结构(6)【AVL树(平衡二叉树)】,计算机基础,# 数据结构,数据结构,AVL树,二叉树

右旋转和左旋转类似,右旋的方式是先将x节点的右子树单独定义出来为t2,然后将y节点连接到x的右子树,将t2连接到y的左子树上,这时就完成了右旋。

    // 右旋转
    private Node rightRotate(Node y) {
        Node x = y.left;
        Node rightX = x.right;
        x.right = y;
        y.left = rightX;
        y.height = 1 + Math.max(getNodeHeight(y.left), getNodeHeight(y.right));
        x.height = 1 + Math.max(getNodeHeight(x.left), getNodeHeight(x.right));
        return x;
    }

3. 右旋+左旋

计算机基础--->数据结构(6)【AVL树(平衡二叉树)】,计算机基础,# 数据结构,数据结构,AVL树,二叉树

所谓右旋+左旋就是将右旋和左旋进行连接,将这类不平衡的树进行平衡

4. 左旋+右旋

计算机基础--->数据结构(6)【AVL树(平衡二叉树)】,计算机基础,# 数据结构,数据结构,AVL树,二叉树

3.判断是否AVL树

判断是否AVL树就是判断二叉树的各个节点的平衡因子是否都是小于等于1或者大于等于-1。文章来源地址https://www.toymoban.com/news/detail-522807.html

    // 判断是否为平衡二叉树
    public boolean isBalanceTree() {
        return isBalanceTree(this.root);
    }

    private boolean isBalanceTree(Node node) {
        if (node == null) {
            return true;
        }
        if (Math.abs(getBalance(node)) > 1) {
            return false;
        }
        return isBalanceTree(node.left) && isBalanceTree(node.right);
    }

 	// 获取当前节点的平衡因子(左右子树高度差)
    private int getBalance(Node node) {
        if (node == null) {
            return 0;
        }
        return getNodeHeight(node.left) - getNodeHeight(node.right);
    }

4.删除节点

    // 删除任意节点
    public void remove(int val) {
        boolean isExist = contains(val);
        if (isExist) {
            this.root = remove(this.root, val);
        }
    }

    // 从以node为根的二分搜索树中删除值为val的结点
    private Node remove(Node node, int val) {
        Node resultNode = null;
        if (node.val == val) {
            // 叶子节点
            if (node.left == null) {
                Node rightNode = node.right;
                node.right = null;
                this.size--;
                resultNode = rightNode;
            } else if (node.right == null) {
                Node leftNode = node.left;
                node.left = null;
                this.size--;
                resultNode = leftNode;
            } else {
                // 找出删除节点的后继
                Node nextNode = getMinNode(node.right);
                // 从右树中删除最小节点
                nextNode.right = removeMinNode(node.right);
                // 开始连接
                nextNode.left = node.left;
                // 让node失去关联关系
                node.left = node.right = null;
                resultNode = nextNode;
            }
        } else if (node.val > val) {
            node.left = remove(node.left, val);
            resultNode = node;
        } else {
            node.right = remove(node.right, val);
            resultNode = node;
        }

        // 删除的是叶子节点
        if (resultNode == null) {
            return null;
        }

        // 删除之后,可能改变了树的平衡,因此需要进行调整
        resultNode.height = Math.max(getNodeHeight(resultNode.left), getNodeHeight(resultNode.right)) + 1;

        Node result = resultNode;
        if (getBalance(resultNode) > 1 && getBalance(resultNode.left) >= 0) {
            result = rightRotate(resultNode);
        } else if (getBalance(resultNode) < -1 && getBalance(resultNode.right) <= 0) {
            result = leftRotate(resultNode);
        } else if (getBalance(resultNode) > 1 && getBalance(resultNode.left) < 0) {
            resultNode.left = leftRotate(resultNode.left);
            result = rightRotate(resultNode);
        } else if (getBalance(resultNode) < -1 && getBalance(resultNode.right) > 0) {
            resultNode.right = rightRotate(resultNode.right);
            result = leftRotate(resultNode);
        }
        return result;
    }

全部代码

import java.util.*;

public class AVLTree {

    private Node root;
    private int size;

    public AVLTree() {
        this.root = null;
        this.size = 0;
    }

    // 判断是否为空
    public boolean isEmpty() {
        return this.size == 0;
    }

    // 获取树中节点个数
    public int getSize() {
        return this.size;
    }

    // 获取节点高度
    public int getNodeHeight(Node node) {
        if (node == null) {
            return 0;
        }
        return node.height;
    }

    // 获取当前节点的平衡因子(左右子树高度差)
    private int getBalance(Node node) {
        if (node == null) {
            return 0;
        }
        return getNodeHeight(node.left) - getNodeHeight(node.right);
    }

    // 查找最小节点
    public Node getMinNode() {
        if (this.root == null) {
            return null;
        }
        return getMinNode(this.root);
    }

    private Node getMinNode(Node node) {
        if (node.left == null) {
            return node;
        }
        return getMinNode(node.left);
    }

    // 判断是否重复
    public boolean contains(int val) {
        return contains(this.root, val);
    }

    private boolean contains(Node node, int val) {
        if (node == null) {
            return false;
        }
        if (node.val == val) {
            return true;
        } else if (node.val > val) {
            return contains(node.left, val);
        } else {
            return contains(node.right, val);
        }
    }

    // 向树中添加节点
    public void add(int val) {
        if (contains(val)) {
            return;
        }
        this.root = add(this.root, val);
        this.size++;
    }

    // 向AVL树中添加节点
    private Node add(Node node, int val) {
        // 递归到底的情况
        if (node == null) {
            return new Node(val);
        }
        if (node.val > val) {
            node.left = add(node.left, val);
        } else {
            node.right = add(node.right, val);
        }
        // 更新节点高度
        node.height = Math.max(getNodeHeight(node.left), getNodeHeight(node.right)) + 1;

        // 维护平衡
        int balance = getBalance(node);
        if (balance > 1 && getBalance(node.left) >= 0) {
            // 右旋
            return rightRotate(node);
        } else if (balance > 1 && getBalance(node.left) <= 0) {
            // 左旋右旋
            node.left = leftRotate(node.left);
            return rightRotate(node);
        } else if (balance < -1 && getBalance(node.right) >= 0) {
            // 右旋左旋
            node.right = rightRotate(node.right);
            return leftRotate(node);
        } else if (balance < -1 && getBalance(node.right) <= 0) {
            // 左旋
            return leftRotate(node);
        }
        return node;
    }

    // 从二分搜索树中删除最小节点
    public Node removeMinNode() {
        if (this.root == null) {
            return null;
        }
        Node result = getMinNode();
        if (result != null) {
            this.root = removeMinNode(this.root);
            this.size--;
        }
        return result;
    }

    private Node removeMinNode(Node node) {
        if (node.left == null) {
            return node.right;
        }
        node.left = removeMinNode(node.left);
        return node;
    }

    // 删除任意节点
    public void remove(int val) {
        boolean isExist = contains(val);
        if (isExist) {
            this.root = remove(this.root, val);
        }
    }

    // 从以node为根的二分搜索树中删除值为val的结点
    private Node remove(Node node, int val) {
        Node resultNode = null;
        if (node.val == val) {
            // 叶子节点
            if (node.left == null) {
                Node rightNode = node.right;
                node.right = null;
                this.size--;
                resultNode = rightNode;
            } else if (node.right == null) {
                Node leftNode = node.left;
                node.left = null;
                this.size--;
                resultNode = leftNode;
            } else {
                // 找出删除节点的后继
                Node nextNode = getMinNode(node.right);
                // 从右树中删除最小节点
                nextNode.right = removeMinNode(node.right);
                // 开始连接
                nextNode.left = node.left;
                // 让node失去关联关系
                node.left = node.right = null;
                resultNode = nextNode;
            }
        } else if (node.val > val) {
            node.left = remove(node.left, val);
            resultNode = node;
        } else {
            node.right = remove(node.right, val);
            resultNode = node;
        }

        // 删除的是叶子节点
        if (resultNode == null) {
            return null;
        }

        // 删除之后,可能改变了树的平衡,因此需要进行调整
        resultNode.height = Math.max(getNodeHeight(resultNode.left), getNodeHeight(resultNode.right)) + 1;

        Node result = resultNode;
        if (getBalance(resultNode) > 1 && getBalance(resultNode.left) >= 0) {
            result = rightRotate(resultNode);
        } else if (getBalance(resultNode) < -1 && getBalance(resultNode.right) <= 0) {
            result = leftRotate(resultNode);
        } else if (getBalance(resultNode) > 1 && getBalance(resultNode.left) < 0) {
            resultNode.left = leftRotate(resultNode.left);
            result = rightRotate(resultNode);
        } else if (getBalance(resultNode) < -1 && getBalance(resultNode.right) > 0) {
            resultNode.right = rightRotate(resultNode.right);
            result = leftRotate(resultNode);
        }
        return result;
    }

    // 中序遍历
    public List<Integer> middleTravel() {
        List<Integer> list = new ArrayList<>();
        middleTravel(this.root, list);
        return list;
    }

    private void middleTravel(Node node, List<Integer> list) {
        if (node == null) {
            return;
        }
        middleTravel(node.left, list);
        list.add(node.val);
        middleTravel(node.right, list);
    }

    // 层序遍历
    private List<Node> levelTravel() {
        List<Node> list = new ArrayList<>();
        Queue<Node> queue = new LinkedList<>();
        if (this.root == null) {
            return list;
        }
        queue.offer(this.root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            list.add(node);
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
        return list;
    }

    // 判断是否是二分搜索树
    public boolean isBinearySearchTree() {
        List<Integer> list = middleTravel();
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i - 1) > list.get(i)) {
                return false;
            }
        }
        return true;
    }

    // 判断是否为平衡二叉树
    public boolean isBalanceTree() {
        return isBalanceTree(this.root);
    }

    private boolean isBalanceTree(Node node) {
        if (node == null) {
            return true;
        }
        if (Math.abs(getBalance(node)) > 1) {
            return false;
        }
        return isBalanceTree(node.left) && isBalanceTree(node.right);
    }

    // 右旋转
    private Node rightRotate(Node y) {
        Node x = y.left;
        Node rightX = x.right;
        x.right = y;
        y.left = rightX;
        y.height = 1 + Math.max(getNodeHeight(y.left), getNodeHeight(y.right));
        x.height = 1 + Math.max(getNodeHeight(x.left), getNodeHeight(x.right));
        return x;
    }

    // 左旋转
    private Node leftRotate(Node y) {
        Node x = y.right;
        Node leftX = x.left;
        x.left = y;
        y.right = leftX;
        y.height = 1 + Math.max(getNodeHeight(y.left), getNodeHeight(y.right));
        x.height = 1 + Math.max(getNodeHeight(x.left), getNodeHeight(x.right));
        return x;
    }

    public String show() {
        // 按层遍历树
        List<Node> list = levelTravel();
        String s = "";
        for (int i = 0; i < list.size(); i++) {
            s += "size = " + list.get(i).val + ", height = " + list.get(i).height + ", balance = " + getBalance(list.get(i)) + ";\n";
        }
        return s;
    }

    @Override
    public String toString() {
        // 按层遍历树
        List<Node> list = levelTravel();
        String s = "["+list.get(0).val;
        for (int i = 1; i < list.size(); i++) {
            s += "," + list.get(i).val;
        }
        s += "]";
        return s;
    }

    private class Node {
        int val;
        Node left;
        Node right;
        int height;

        public Node(int val) {
            this.val = val;
            this.left = this.right = null;
            this.height = 1;
        }
    }

    public static void main(String[] args) {
        AVLTree avlTree = new AVLTree();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            avlTree.add(i);
            avlTree.add(random.nextInt(100));
        }
        System.out.println(avlTree.toString());
        // 判断是否为平衡二叉树
        System.out.println(avlTree.isBalanceTree());
        avlTree.removeMinNode();
        System.out.println(avlTree.toString());
        avlTree.remove(9);
        System.out.println(avlTree.toString());

    }
}

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

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

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

相关文章

  • 计算机复试面试基础知识(八股文)(数据库、数据结构、操作系统、计网、机组等)

    数据库绪论 1、简述三层模式、两级映射,分别有什么作用? 模式(逻辑模式):是数据库中全体数据的逻辑结构和特征的描述,是数据库系统模式结构的中间层,即不涉及数据的物理存储细节,也与具体应用程序开发工具语言无关。 外模式(用户模式):是用户能看见和使

    2023年04月09日
    浏览(118)
  • 系统架构设计师---计算机基础知识之数据库系统结构与规范化

    目录 一、基本概念  二、 数据库的结构  三、常用的数据模型         概念数据模型        基本数据模型        面向对象模型 四、数据的规范化      函数依赖       范式   1. 数据库 (DataBase, DB) : 是指长期储存在计算机内的、有组织的、可共享的数据集合。   

    2024年02月12日
    浏览(54)
  • 计算机导论07-算法和数据结构

    算法是 为使用计算机解决问题而制定的运算序列,是解决实际问题的方法及步骤 ;在计算机科学中,算法研究应用计算机程序处理问题的方法及其实现流程,是计算机问题解决方案的完整描述, 它是计算机科学的核心研究对象之一。 算法的概念 一般认为,算法(algorithm)

    2024年01月20日
    浏览(50)
  • 数据结构与算法:计算机科学的基石

    🎉欢迎来到数据结构学习专栏~数据结构与算法:计算机科学的基石 ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹 ✨博客主页:IT·陈寒的博客 🎈该系列文章专栏:数据结构学习 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 🍹文章作者技术和水平有限,如果文中

    2024年02月11日
    浏览(55)
  • 【2023计算机考研】初试数据结构的院校汇总

    PS:学校具体考研信息在院校信息中输入学校名称搜索可查看 传送门:学校列表 - N诺计算机考研 专硕 北方工业大学 北京工商大学 北京石油化工学院 北京电子科技学院 中国农业大学 中央财经大学 北京物资学院 中央民族大学 天津职业技术师范大学 河北科技大学 石家庄铁道

    2024年02月13日
    浏览(71)
  • 只考一门数据结构!安徽工程大学计算机考研

    安徽工程大学 考研难度(☆) 内容: 23考情概况(拟录取和复试分析) 、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文992字,预计阅读:3分钟 2023考情概况 安徽工程大学计算机相关各专业复试和拟录取分析: 083500软件工程一志愿拟录取12人

    2024年02月10日
    浏览(43)
  • 王道计算机考研 数据结构C语言复现-第六章-队列

     这篇文章收录了王道考研课程中涉及的数据结构的所有代码。此外,本博客可能会添加一些额外的代码(不仅限于王道考研),因为408考试中会频繁考察一些冷门的知识点,所以这篇博客会涵盖所有相关的代码。这也是我数据结构的第一轮复习,希望能与大家共同进步。由

    2024年01月21日
    浏览(43)
  • 计算机保研面试常见问题(408数据结构简答题)

    1. 什么是时间复杂度?O(n)的O代表了什么? 答:时间复杂度是指执行算法所需要的计算工作量,可以用于描述一个程序的规模。O(n)中的O表示的是最坏情况下的时间复杂度。 2. 时间复杂度的量级排序是什么? 答:O(logn) O(n) O(nlogn) O(n^2) O(2^n) O(n!) 3. 顺

    2024年02月13日
    浏览(61)
  • 【23考研】计算机408数据结构代码题强化阶段划重点(王道书)

    视频链接:【23考研】10分钟带你整理408数据结构强化阶段代码题复习重点 本篇只适合考408的同学,请自主命题的同学自觉右上角×掉 因为王道书为了照顾自主命题的同学,所以很多算法也给出了代码实现,实际上对于考408的同学,很多代码是不需要掌握的,毕竟408的代码题没

    2024年02月15日
    浏览(49)
  • 【24计算机择校】37所只考数据结构的985/211院校汇总

    最新数据见:【24计算机择校】37所只考数据结构的985/211院校汇总_综合_N诺计算机考研 适合零基础和跨考的同学,不建议零基础的同学考408,如果只想上岸的话可以保持关注哦~ 注意:以下 数据来源23考研初试专业课情况 ,24考研可能会有部分院校改考,大家要及时关注我们

    2024年02月16日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包