AVL树

 AVL树是一个平衡二叉搜索树。AVL树的 高度 的两个 孩子 节点的子树最多相差一个,如果在任何时候他们相差不止一个,再平衡完成恢复这个属性。

节点的平衡因子是它的右子树的高度减去它的左子树的高度。带有平衡因子 1、0 或 -1 的节点被认为是平衡的。带有平衡因子 -2 或 2 的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。


我们再来看看它的效率:


以上,我们便了解了AVL树,那么我们就知道,如果要保持平衡因子的的平衡,从而使整个树保持平衡,是不容易的,那么怎样才能保持平衡呢?

在维基百科中是这样说的,它叫这种插入叫做追溯循环的插入。

我们首先不要关注它为什么叫这个名字,我们先看看它有几种情况


以上便是第一种情况:

<span style="font-size:18px;">void _RotateL(Node* &parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;

		if(subRL)
			subRL->_parent = parent;
		subR->_left = parent;
		subR->_parent = parent->_parent;
		parent->_parent = subR;

		parent = subR;
		parent->bf = 0;
	}</span>



<span style="font-size:18px;">void _RotateR(Node* &parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if(subLR)
			subLR->_parent = parent;
		subL->_right = parent;
		subL->_parent = parent->_parent;
		parent->_parent = subL;
		parent =subL;
		parent->bf = 0;
	}</span>


这种情况就是先左旋转然后右旋转,所以最初,我是这样写的

<span style="font-size:18px;">void _RotateLR(Node*& parent)
	{
		</span><pre name="code" class="cpp"><span style="font-size:18px;">               _RotateL(parent);</span>
<pre name="code" class="cpp"><span style="font-size:18px;">             _RotateR(parent);</span>

 
} 
 
但是,在某种特殊的情况下,它的平衡因子是不对的,所以我采用了,下面的办法补救

<span style="font-size:18px;">void _RotateLR(Node*& parent)
	{
		Node* pNode = parent;
		Node* subLNode = parent->_left;
		Node* subLRNode = subLNode->_right;
		int _bf = subLRNode->bf;

		_RotateL(parent->_left);
		_RotateR(parent);

		if (_bf == -1)
		{
			subLNode->bf = 0;
			pNode->bf = 1;
		}
		else if (_bf == 1)
		{
			subLNode->bf = -1;
			pNode->bf = 0;
		}
		else
		{
			subLNode->bf = 0;
			pNode->bf = 0;
		}

		subLRNode->bf = 0;
	}</span>


同理,我也就不把最初的代码给出来了,直接给新的

<span style="font-size:18px;">void _RotateRL(Node*& parent)
	{
		Node* pNode = parent;
		Node* subRNode = parent->_right;
		Node* subRLNode = subRNode->_left;
		int _bf = subRLNode->bf;

		_RotateR(parent->_right);
		_RotateL(parent);

		if (_bf == 1)
		{
			subRNode->bf = 0;
			pNode->bf = -1;
		}
		else if (_bf == -1)
		{
			subRNode->bf = 1;
			pNode->bf = 0;
		}
		else
		{	
			subRNode->bf = 0;
			pNode->bf = 0;
		}

		subRLNode->bf = 0;
	}</span>


接下来,我们说说它的删除

让节点D是我们要删除的节点,并让树中的节点E是一个节点我们需要找到节点D的位置,让节点N是我们拿出实际的节点树。
步骤时要考虑删除一个节点在一个AVL树如下:
1.如果节点D是一片叶子或只有一个孩子,跳到步骤6 N:= D、G D和的父母 dir 孩子的方向在G D。
2.否则,确定E的遍历的节点最左边的(最小) [9] D节点的右子树(D的顺序继承人−没有左子),或者最右边的在它的左子树(D的顺序的前任没有右子)。
3.记得节点G,E的父母。
4.提供节点E与所有节点D的孩子和家长联系,让节点E所有孩子和家长联系以前的新目标指向D在这一步中,节点之间的顺序序列D和E是暂时的干扰,但树结构并没有改变。
5.让N:= E, dir :=左)和F(左)孩子的E。
6.代替N在孩子的位置 dir 由其孩子F,G是null或叶。 在后一种情况下设置其母G。 
(现在的最后痕迹D已经从树中删除,所以D可能从内存中删除。)
7.如果节点N根(其母G是null),更新根。
8.Otherweise的高度 dir 子树G已经下降了1,从1到0或从2比1。 所以,让X:= G和N dir 孩子在下面的代码块(X),为了追溯路径备份根的树,从而调整平衡因素(包括可能的旋转)。


因为与单个删除AVL子树的高度不能减少一个以上的,暂时的平衡因素的一个节点将从−2 + 2。
如果平衡因素成为±2子树是不平衡的,需要旋转。 介绍了各种情况下的旋转部分 再平衡 。
通过删除节点N X的子树N的高度已经下降了1,从1到0或从2比1。


不变的追溯循环删除
由N棵子树的高度已经下降了1。 它已经在AVL形状。

<span style="font-size:18px;">for (X = parent(N); X != null; X = G) { // Loop (possibly up to the root)
     G = parent(X); // Save parent of X around rotations
     // BalanceFactor(X) has not yet been updated!
     if (N == left_child(X)) { // the left subtree decreases
         if (BalanceFactor(X) > 0) { // X is right-heavy
             // ===> the temporary BalanceFactor(X) == +2
             // ===> rebalancing is required.
             Z = right_child(X); // Sibling of N (higher by 2)
             b = BalanceFactor(Z);
             if (b < 0)                     // Right Left Case     (see figure 5)
                 N = rotate_RightLeft(X,Z); // Double rotation: Right(Z) then Left(X)
             else                           // Right Right Case    (see figure 4)
                 N = rotate_Left(X,Z);      // Single rotation Left(X)
             // After rotation adapt parent link
         }
         else {
             if (BalanceFactor(X) == 0) {
                 BalanceFactor(X) = +1; // N’s height decrease is absorbed at X.
                 break; // Leave the loop
             }
             N = X;
             BalanceFactor(N) = 0; // Height(N) decreases by 1
             continue;
         }
     }
     else { // (N == right_child(X)): The right subtree decreases
         if (BalanceFactor(X) < 0) { // X is left-heavy
             // ===> the temporary BalanceFactor(X) == –2
             // ===> rebalancing is required.
             Z = left_child(X); // Sibling of N (higher by 2)
             b = BalanceFactor(Z);
             if (b > 0)                     // Left Right Case
                 N = rotate_LeftRight(X,Z); // Double rotation: Left(Z) then Right(X)
                else                        // Left Left Case
                 N = rotate_Right(X,Z);     // Single rotation Right(X)
             // After rotation adapt parent link
         }
         else {
             if (BalanceFactor(X) == 0) {
                 BalanceFactor(X) = –1; // N’s height decrease is absorbed at X.
                 break; // Leave the loop
             }
             N = X;
             BalanceFactor(N) = 0; // Height(N) decreases by 1
             continue;
         }
     }
     // After a rotation adapt parent link:
     // N is the new root of the rotated subtree
     parent(N) = G;
     if (G != null) {
         if (X == left_child(G))
             left_child(G) = N;
         else
             right_child(G) = N;
         if (b == 0)
             break; // Height does not change: Leave the loop
     }
     else {
         tree->root = N; // N is the new root of the total tree
         continue;
     }
     // Height(N) decreases by 1 (== old Height(X)-1)
 }
 // Unless loop is left via break, the height of the total tree decreases by 1.</span>
追溯可以停止如果平衡因素变成了±1意味着子树的高度保持不变。
如果平衡因素变成了0,那么子树的高度降低,追溯需要继续。
如果暂时的平衡因素成为±2,这是修复了一个适当的旋转。 这取决于的平衡因子兄弟Z(更高的子树)是否子树的高度减少一个或不会改变(后者,如果Z因子平衡0)。
所需的时间是 O(log n ) 查找,再加上最多 O(log n ) 追溯水平( O(1) 平均)回到根,所以可以完成操作 O(log n ) 时间。
(ps:因为删除逻辑太多,所以以上是维基百科上的,网读者见谅)

AVL树和红黑树都是自平衡二叉搜索树,他们在数学上非常相似。 平衡树的操作不同,但平均发生 O(1) 最大的 O(log n ) 。 真正的两者之间的区别是极限高度。
树的大小 n ≥1


AVL树比“红-黑”树更严格的平衡,导致更快的检索,但缓慢的插入和删除。



完整的代码在点击打开链接





【轴承故障诊断】基于融合鱼鹰和柯西变异的麻雀优化算法OCSSA-VMD-CNN-BILSTM轴承诊断研究【西储大学数据】(Matlab代码实现)内容概要:本文提出了一种基于融合鱼鹰和柯西变异的麻雀优化算法(OCSSA)优化变分模态分解(VMD)参数,并结合卷积神经网络(CNN)与双向长短期记忆网络(BiLSTM)的轴承故障诊断模型。该方法利用西储大学公开的轴承数据集进行验证,通过OCSSA算法优化VMD的分解层数K和惩罚因子α,有效提升信号分解精度,抑制模态混叠;随后利用CNN提取故障特征的空间信息,BiLSTM捕捉时间序列的动态特征,最终实现高精度的轴承故障分类。整个诊断流程充分结合了信号预处理、智能优化与深度学习的优势,显著提升了复杂工况下轴承故障诊断的准确性与鲁棒性。; 适合人群:具备一定信号处理、机器学习及MATLAB编程基础的研究生、科研人员及从事工业设备故障诊断的工程技术人员。; 使用场景及目标:①应用于旋转机械设备的智能运维与故障预警系统;②为轴承等关键部件的早期故障识别提供高精度诊断方案;③推动智能优化算法与深度学习在工业信号处理领域的融合研究。; 阅读建议:建议读者结合MATLAB代码实现,深入理解OCSSA优化机制、VMD参数选择策略以及CNN-BiLSTM网络结构的设计逻辑,通过复现实验掌握完整诊断流程,并可进一步尝试迁移至其他设备的故障诊断任务中进行验证与优化。
07-07
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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值