AVL树

二叉平衡树

二叉搜索树
二叉搜索树又称二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树:
(1):对于它的任意一个结点而言,若该结点的左子树不为空,则左子树上所有结点的值小于该结点的值.。
(1):对于它的任意一个结点而言,若该结点的右子树不为空,则右子树上所有结点的值大于该结点的值.。
即对于他的任意一个结点而言,该结点的左右子树也分别为二叉搜索树

二叉搜索树的查找
既然将其称之为二叉搜索树,因此这棵树最主要的作用是进行查询,而且其查询原理特别简单,具体如下:
若根节点不为空:
若根节点key == 查找key,返回true
若根节点key大于查找key,在其左子树查找
若根节点key小于查找key,在其右子树查找
若最终仍未找到,返回false

插入和删除操作,也都是建立在查找的基础之上的,理想情况下,二叉搜索树的查找时间复杂度为O(log(n)),但是,在最坏的情况下(对于任意一个节点,都只有左孩子或者右孩子的情况下),他的时间复杂度为O(n),进而导致插入和删除的时间复杂度也变为O(n)

为了解决上述问题,引入了平衡树的概念,平衡树是指任意结点,左右子树的高度差都小于等于 1 ,AVL 树就是一个二叉平衡搜索树

AVL树
由上述我们就知道,AVL树是在二叉搜索树的基础上要求,任意结点左右子树的高度差不超过 1,并将高度差命名为平衡因子(Balance Factor),二叉树的结点代码如下:

class Node {
	private K key;
	private V value;  // K-V模型带着value,纯key模型可以不带value

	Node<K,V> left;
	Node<K,V> right;

	int bf;  //平衡因子
	Node<K,V> parent; // 保存自己的双亲结点,如果是跟,则为null
}

我们具体讲AVL树插入的过程:
1.按照普通搜索树的方式进行插入,若 key 重复,则放弃插入
2.插入后依次往上修改平衡因子,修改的原则为:
如果插入的地方为该结点的左孩子,则平衡因子 - 1
如果插入的地方为该结点的右孩子,则平衡因子 + 1
因此,在插入前,AVL树的平衡因子的取值范围为 [ -1, 0, 1 ],插入后,因为平衡因子被修改,所以,平衡因子的取值范围变为 [ -2,-1, 0, 1 ,2],

如果被修复后,平衡因子为 0 ,则表示这棵树的高度插入前后未改变
在这里插入图片描述
因为该结点的的平衡因子为 0 ,,高度未改变,不会继续影响该结点的的父结点的平衡因子,所以插入过程结束,整棵树仍是AVL树

如果修改后平衡因子 == -1 / 1,则说明高度发生了变化,增加了 1
在这里插入图片描述
所以该结点的父节点的平衡因子也需要被调整
在这里插入图片描述
也就是说,只要平衡因子调节完的结果是 -1/1,则调整平衡因子的过程会向上蔓延

如果被修改后的平衡因子的值为 -2 或者 2 ,则说明AVL树的特征被改变了,那么我们就需要利用规则进行修复。在修复之前,任意时刻,一个AVL树最多只有一个结点的平衡因子不满足特征,修复子树的过程会保证这颗子树的高度不变,这样不至于影响到该子树父结点的平衡因子。
在这里插入图片描述
AVL树把破坏规则的情况称为失衡(失去平衡),失衡的情况分为 4 类;

1. 左左失衡: parent 的失衡是因为在 parent 的左孩子的左子树中插入导致的失衡,如下:

在这里插入图片描述
针对左左失衡的情况,只需要对parent(失衡的结点)进行右旋即可
在这里插入图片描述

2.左右失衡:parent 的失衡是因为在 parent 的左孩子的右子树中插入导致的失衡
在这里插入图片描述
针对左右失衡的情况,先对 cur 进行左旋,再对 parent 进行右旋
在这里插入图片描述
3.右右失衡:parent 的失衡是因为在 parent 的右孩子的右子树中插入导致的失衡
针对右右失衡的情况,与左左失衡类似,只需要对parent(失衡的结点)进行左旋即可

2.右左失衡:parent 的失衡是因为在 parent 的右孩子的左子树中插入导致的失衡
针对右左失衡的情况,先对 cur 进行右旋,再对 parent 进行左旋

AVL树性能分析
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即O(log(n))。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

AVL是一种自平衡的二叉查找,它确保了的高度始终保持在对数级别,从而保证了查找、插入和删除操作的时间复杂度为O(log n)。这种数据结构以它的发明者G.M. Adelson-Velsky和E.M. Landis的名字命名[^1]。 ### AVL的定义 - **平衡因子**:每个节点都有一个平衡因子,它是该节点左子的高度减去右子的高度。对于AVL来说,所有节点的平衡因子只能是-1, 0或1。 - **空**:如果T是空,则它自然是一个AVL。 - **非空**:若T不是空,则其左右子TL和TR都必须是AVL,并且对于任意节点,|HL - HR| ≤ 1(其中HL和HR分别表示左子和右子的高度)。 ### AVL的操作 当进行插入或删除操作时,可能会破坏AVL的平衡性,这时需要通过旋转来重新恢复平衡: - **单旋转**: - 左单旋(Single Rotate with Left)用于处理左孩子的左子过高。 - 右单旋(Single Rotate with Right)用于处理右孩子的右子过高。 - **双旋转**: - 左右双旋(Double Rotate with Left)用于处理左孩子的右子过高的情况。 - 右左双旋(Double Rotate with Right)用于处理右孩子的左子过高的情况。 这些旋转操作可以保持AVL的性质不变,并且能够快速地调整的结构以维持平衡。 ### AVL的实现 下面是一个简单的C语言代码示例,展示了如何定义AVL的节点以及实现基本的插入操作。这个例子中包含了必要的函数声明和一些核心逻辑。 ```c #include <stdio.h> #include <stdlib.h> // 定义AVL节点结构体 typedef struct AvlNode { int element; struct AvlNode *left; struct AvlNode *right; int height; // 节点的高度 } *AvlTree, *Position; // 获取两个整数中的较大者 int max(int a, int b) { return (a > b) ? a : b; } // 计算给定节点的高度 static int Height(AvlTree T) { if (T == NULL) return -1; // 空节点高度为-1 else return T->height; } // 单旋转 - 左左情况 AvlTree singlerotatewithLeft(AvlTree K2) { Position K1 = K2->left; K2->left = K1->right; K1->right = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->left), K2->height) + 1; return K1; // 新根 } // 单旋转 - 右右情况 AvlTree singlerotatewithRight(AvlTree K2) { Position K1 = K2->right; K2->right = K1->left; K1->left = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->right), K2->height) + 1; return K1; // 新根 } // 双旋转 - 左右情况 AvlTree doublerotatewithLeft(AvlTree K3) { K3->left = singlerotatewithRight(K3->left); return singlerotatewithLeft(K3); } // 双旋转 - 右左情况 AvlTree doublerotatewithRight(AvlTree K3) { K3->right = singlerotatewithLeft(K3->right); return singlerotatewithRight(K3); } // 插入新元素到AVLAvlTree Insert(int x, AvlTree T) { if (T == NULL) { // 如果为空,创建新节点 T = (AvlTree)malloc(sizeof(struct AvlNode)); if (T == NULL) printf("Out of space!!!"); else { T->element = x; T->left = T->right = NULL; T->height = 0; // 新叶节点高度为0 } } else if (x < T->element) { // 向左子插入 T->left = Insert(x, T->left); // 检查并修复平衡 if (Height(T->left) - Height(T->right) == 2) { if (x < T->left->element) T = singlerotatewithLeft(T); // 左左旋转 else T = doublerotatewithLeft(T); // 左右旋转 } } else if (x > T->element) { // 向右子插入 T->right = Insert(x, T->right); // 检查并修复平衡 if (Height(T->right) - Height(T->left) == 2) { if (x > T->right->element) T = singlerotatewithRight(T); // 右右旋转 else T = doublerotatewithRight(T); // 右左旋转 } } // 更新高度 T->height = max(Height(T->left), Height(T->right)) + 1; return T; } ``` 上述代码提供了AVL的基本框架,包括节点定义、插入操作及必要的旋转方法。实际应用中可能还需要添加更多的功能,如删除节点、查找特定值等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

运笔如飞

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值