数据结构(6)树形结构——平衡二叉树(JAVA代码实现)

简介: 6.1.概述二叉搜索树存在一个问题,就是树的姿态和数据的插入顺序是有关系的,有时候树会变成某一边的子树高度过高,甚至直接退化成斜二叉树,使得查找从二分查找跌落为顺序查找:

6.1.概述

二叉搜索树存在一个问题,就是树的姿态和数据的插入顺序是有关系的,有时候树会变成某一边的子树高度过高,甚至直接退化成斜二叉树,使得查找从二分查找跌落为顺序查找:


78478a0e162b4523aa75f38869a1f22c.png

保证任意结点左右子树的高度一致,便可以保证树的查询效率为最优,但是此种情况过于理想,难以达到,因此允许左右子树的高度间存在差异,于是出现了平衡二叉树,即任意结点左右子树高度差不超过1:

e842419fc8c74553bf7fd45791746cb2.png

每次操作后出现有结点的左右子树高度差超过1的情况时,树会自己进行调整姿态,重新达到平衡。

平衡二叉树只是一种思想,有很多种实现,常见的实现有红黑树、AVL、Treap、伸展树等。

6.2.AVL树

6.2.1.概述

AVL是出现的第一种平衡二叉树,每当插入元素,造成AVL树的不平衡后,它会通过旋转的方式调整最小不平衡树,从而将树调整平衡。插入后造成不平衡的元素叫“破坏者”,最小不平衡树的根节点叫“被破坏者”。


最小不平衡树,即从高度差超过1的两条分支开始向上找,找到它们的第一个共同父结点,以这个父节点为根结点的子树就是最小不平衡树。


AVL的旋转有四种:


RR旋转

LL旋转

LR旋转

RL旋转

6.2.2.旋转

1.RR旋转

“破坏者”在右子树的右子树,执行RR旋转,将“被破坏者”的右孩子提为根节点,该右孩子的左子树移植为“被破坏者”的右子树。


c1f83426ffba4a7c80d51af5155e4953.png

2.LL旋转

“破坏者”在左子树的左子树,就执行LL旋转,将“被破坏者”压为“破坏者”父节点的右孩子,“破坏者”父节点往上走一层。

7a0990ec263e4b70bd14c7acb7925c94.png

3.LR旋转

破坏者在左子树的右子树,就执行LR旋转,步骤和LL旋转相同,将“被破坏者”压为“破坏者”父节点的右孩子,“破坏者”父节点往上走一层。

c794494e51b441c499f393e066193836.png

4.RL旋转

破坏者在右子树的左子树执行RL旋转,调整被破坏者,被破坏者的R,以及被破坏者R的L(这里可能有点晕,其实仔细观察会发现其实就是契合右子树的左子树这个位置关系。),被破坏者的R提上,被破坏者的R的L不变。


ff86190cdf464d40a151f1ab296e5759.png

6.2.3.代码实现

public class AvlTree<T extends Comparable<? super T>> {
    private AvlNode<T> root;
    public void insert(T x) {
        root = insert(x, root);
    }
    public void remove(T x) {
        root = remove(x, root);
    }
    public T findMin() {
        return findMin(root).element;
    }
    public void makeEmpty() {
        root = null;
    }
    public boolean isEmpty() {
        return root == null;
    }
    /**
     * 添加节点
     *
     * @param x 插入节点
     * @param t 父节点
     */
    private AvlNode<T> insert(T x, AvlNode<T> t) {
        //如果根节点为空,则当前x节点为根及诶单
        if (null == t) {
            return new AvlNode(x);
        }
        int compareResult = x.compareTo(t.element);
        //小于当前根节点 将x插入根节点的左边
        if (compareResult < 0) {
            t.left = insert(x, t.left);
        } else if (compareResult > 0) {
            //大于当前根节点 将x插入根节点的右边
            t.right = insert(x, t.right);
        } else {
        }
        return balance(t);
    }
    private static final int ALLOWED_IMBALANCE = 1;
    private AvlNode<T> balance(AvlNode<T> t) {
        if (t == null) {
            return t;
        }
        if (height(t.left) - height(t.right) > ALLOWED_IMBALANCE) {
            if (height(t.left.left) >= height(t.left.right)) {
                t = rotateWithLeftChild(t);
            } else {
                t = doubleWithLeftChild(t);
            }
        } else if (height(t.right) - height(t.left) > ALLOWED_IMBALANCE) {
            if (height(t.right.right) >= height(t.right.left)) {
                t = rotateWithRightChild(t);
            } else {
                t = doubleWithRightChild(t);
            }
        }
        t.height = Math.max(height(t.left), height(t.right)) + 1;
        return t;
    }
    private AvlNode<T> doubleWithRightChild(AvlNode<T> k3) {
        k3.right = rotateWithLeftChild(k3.right);
        return rotateWithRightChild(k3);
    }
    private AvlNode<T> rotateWithRightChild(AvlNode<T> k2) {
        AvlNode k1 = k2.right;
        k2.right = k1.left;
        k1.left = k2;
        k2.height = Math.max(height(k2.right), height(k2.left)) + 1;
        k1.height = Math.max(height(k1.right), k2.height) + 1;
        return k1;
    }
    private AvlNode<T> doubleWithLeftChild(AvlNode<T> k3) {
        k3.left = rotateWithRightChild(k3.left);
        return rotateWithLeftChild(k3);
    }
    private AvlNode<T> rotateWithLeftChild(AvlNode<T> k2) {
        AvlNode 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 int height(AvlNode<T> t) {
        return t == null ? -1 : t.height;
    }
    /**
     * 删除节点
     *
     * @param x    节点
     * @param t    父节点
     */
    private AvlNode<T> remove(T x, AvlNode<T> t) {
        if (null == t) {
            return t;
        }
        int compareResult = x.compareTo(t.element);
        //小于当前根节点
        if (compareResult < 0) {
            t.left = remove(x, t.left);
        } else if (compareResult > 0) {
            //大于当前根节点
            t.right = remove(x, t.right);
        } else if (t.left != null && t.right != null) {
            //找到右边最小的节点
            t.element = findMin(t.right).element;
            //当前节点的右边等于原节点右边删除已经被选为的替代节点
            t.right = remove(t.element, t.right);
        } else {
            t = (t.left != null) ? t.left : t.right;
        }
        return balance(t);
    }
    /**
     * 找最小节点
     *
     * @param root 根节点
     */
    private AvlNode<T> findMin(AvlNode<T> root) {
        if (root == null) {
            return null;
        } else if (root.left == null) {
            return root;
        }
        return findMin(root.left);
    }
    /**
     * 找最大节点
     *
     * @param root 根节点
     */
    private AvlNode<T> findMax(AvlNode<T> root) {
        if (root == null) {
            return null;
        } else if (root.right == null) {
            return root;
        } else {
            return findMax(root.right);
        }
    }
    public void printTree() {
        if (isEmpty()) {
            System.out.println("节点为空");
        } else {
            printTree(root);
        }
    }
    public void printTree(AvlNode<T> root) {
        if (root != null) {
            System.out.print(root.element);
            if (null != root.left) {
                System.out.print("左边节点" + root.left.element);
            }
            if (null != root.right) {
                System.out.print("右边节点" + root.right.element);
            }
            System.out.println();
            printTree(root.left);
            printTree(root.right);
        }
    }
}


目录
相关文章
|
13天前
|
Java
java数据结构,双向链表的实现
文章介绍了双向链表的实现,包括数据结构定义、插入和删除操作的代码实现,以及双向链表的其他操作方法,并提供了完整的Java代码实现。
java数据结构,双向链表的实现
|
13天前
|
存储 Java
java数据结构,线性表链式存储(单链表)的实现
文章讲解了单链表的基本概念和Java实现,包括头指针、尾节点和节点结构。提供了实现代码,包括数据结构、接口定义和具体实现类。通过测试代码演示了单链表的基本操作,如添加、删除、更新和查找元素,并总结了操作的时间复杂度。
java数据结构,线性表链式存储(单链表)的实现
|
8天前
|
存储 安全 Java
Java 数据结构类型总结
在 Java 中,常用的数据结构包括基础数据结构(如数组和字符串)、集合框架(如 Set、List 和 Map 接口的多种实现)、特殊数据结构(如栈、队列和双端队列)、链表(单链表、双链表和循环链表)以及图和树等。这些数据结构各有特点和适用场景,选择时需考虑性能、内存和操作需求。集合框架提供了丰富的接口和类,便于处理对象集合。
|
1天前
|
算法 开发者 计算机视觉
燃爆全场!Python并查集:数据结构界的网红,让你的代码炫酷无比!
在编程的世界里,总有一些数据结构以其独特的魅力和高效的性能脱颖而出,成为众多开发者追捧的“网红”。今天,我们要介绍的这位明星,就是Python中的并查集(Union-Find)——它不仅在解决特定问题上大放异彩,更以其优雅的设计和强大的功能,让你的代码炫酷无比,燃爆全场!
7 0
|
1天前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
5 0
|
13天前
|
存储 Java
java数据结构,线性表顺序存储(数组)的实现
文章介绍了Java中线性表顺序存储(数组)的实现。线性表是数据结构的一种,它使用数组来实现。文章详细描述了线性表的基本操作,如增加、查找、删除、修改元素,以及其他操作如遍历、清空、求长度等。同时,提供了完整的Java代码实现,包括MyList接口和MyLinearList实现类。通过main函数的测试代码,展示了如何使用这些方法操作线性表。
|
12天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
30 2
|
4天前
|
Java 数据库 UED
Java的多线程有什么用
Java的多线程技术广泛应用于提升程序性能和用户体验,具体包括:提高性能,通过并行执行充分利用多核CPU;保持响应性,使用户界面在执行耗时操作时仍流畅交互;资源共享,多个线程共享同一内存空间以协同工作;并发处理,高效管理多个客户端请求;定时任务,利用`ScheduledExecutorService`实现周期性操作;任务分解,将大任务拆分以加速计算。多线程尤其适用于高并发和并行处理场景。
|
16天前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理
|
20天前
|
缓存 Java 应用服务中间件
Java虚拟线程探究与性能解析
本文主要介绍了阿里云在Java-虚拟-线程任务中的新进展和技术细节。