建设项目环境影响网站免费crm客户管理软件
web/
2025/10/3 11:48:54/
文章来源:
建设项目环境影响网站,免费crm客户管理软件,东莞市建设工程监督网,asp网页编辑器高度平衡二叉搜索树 AVL树的概念1.操作2.删除3.搜索4.实现描述 AVL树的实现1.AVL树节点的定义2.AVL树的插入3.AVL树的旋转3.1 新节点插入较高右子树的右侧---右右:左单旋3.2 新节点插入较高左子树的左侧---左左:右单旋3.3 新节点插入较高左子树的右侧---左右#xff1a;先左单… 高度平衡二叉搜索树 AVL树的概念1.操作2.删除3.搜索4.实现描述 AVL树的实现1.AVL树节点的定义2.AVL树的插入3.AVL树的旋转3.1 新节点插入较高右子树的右侧---右右:左单旋3.2 新节点插入较高左子树的左侧---左左:右单旋3.3 新节点插入较高左子树的右侧---左右先左单旋再右单旋3.4 新节点插入较高右子树的左侧---右左先右单旋再左单旋 4.AVL树的验证4.1 验证其为二叉搜索树4.2 验证其为平衡树4.3 验证用例 AVL树实现及验证所有代码1.AVL代码实现2.AVL验证代码实现 AVL树的概念
AVL树Adelson-Velsky and Landis Tree是计算机科学中最早被发明的自平衡二叉查找树。在AVL树中任一节点对应的两棵子树的最大高度差为1因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是O(logN)。增加和删除元素的操作则可能需要借由一次或多次树旋转以实现树的重新平衡。AVL树得名于它的发明者G. M. Adelson-Velsky和Evgenii Landis他们在1962年的论文《An algorithm for the organization of information》中公开了这一数据结构。
节点的平衡因子是它的左子树的高度减去它的右子树的高度有时相反。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的并需要重新平衡这个树。平衡因子可以直接存储在每个节点中或从可能存储在节点中的子树高度计算出来。
算法平均最差空间O(n)O(n)搜索O(log n)O(log n)插入O(log n)O(log n)删除O(log n)O(log n)
1.操作
AVL树的基本操作一般涉及运作同在不平衡的二叉查找树所运作的同样的算法。但是要进行预先或随后做一次或多次所谓的AVL旋转。
以下图表以四列表示四种情况每行表示在该种情况下要进行的操作。在左左和右右的情况下只需要进行一次旋转操作在左右和右左的情况下需要进行两次旋转操作。 下面动画演示了不断将节点插入AVL树时的情况并且演示了左旋Left Rotation、右旋Right Rotation、右左旋转Right-Left Rotation、左右旋转Left-Right Rotation以及带子树的右旋Right Rotation with children
2.删除
从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点接着直接移除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转而每次AVL旋转耗费固定的时间所以删除处理在整体上耗费O(log n) 时间。
3.搜索
可以像普通二叉查找树一样的进行所以耗费O(log n)时间因为AVL树总是保持平衡的。不需要特殊的准备树的结构不会由于查找而改变。这是与伸展树搜索相对立的它会因为搜索而变更树结构。
4.实现描述
假设平衡因子是左子树的高度减去右子树的高度所得到的值又假设由于在二叉排序树上插入节点而失去平衡的最小子树根节点的指针为a即a是离插入点最近且平衡因子绝对值超过1的祖先节点则失去平衡后进行的规律可归纳为下列四种情况
单向右旋平衡处理LL由于在*a的左子树根节点的左子树上插入节点a的平衡因子由1增至2致使以a为根的子树失去平衡则需进行一次右旋转操作单向左旋平衡处理RR由于在*a的右子树根节点的右子树上插入节点a的平衡因子由-1变为-2致使以a为根的子树失去平衡则需进行一次左旋转操作双向旋转先左后右平衡处理LR由于在*a的左子树根节点的右子树上插入节点a的平衡因子由1增至2致使以a为根的子树失去平衡则需进行两次旋转先左旋后右旋操作。双向旋转先右后左平衡处理RL由于在*a的右子树根节点的左子树上插入节点a的平衡因子由-1变为-2致使以a为根的子树失去平衡则需进行两次旋转先右旋后左旋操作。
在平衡二叉排序树AVL树Adelson-Velsky and Landis Tree上插入一个新的数据元素e的递归算法可描述如下
若AVL树为空树则插入一个数据元素为e的新节点作为AVL树的根节点树的深度增1若e的关键字和AVL树的根节点的关键字相等则不进行若e的关键字小于AVL树的根节点的关键字而且在AVL树的左子树中不存在和e有相同关键字的节点则将e插入在AVL树的左子树上并且当插入之后的左子树深度增加1时分别就下列不同情况处理之 AVL树的根节点的平衡因子为-1右子树的深度大于左子树的深度则将根节点的平衡因子更改为0BBST的深度不变AVL树的根节点的平衡因子为0左、右子树的深度相等则将根节点的平衡因子更改为1BBST的深度增1AVL树的根节点的平衡因子为1左子树的深度大于右子树的深度则若AVL树的左子树根节点的平衡因子为1则需进行单向右旋平衡处理并且在右旋处理之后将根节点和其右子树根节点的平衡因子更改为0树的深度不变 若e的关键字大于AVL树的根节点的关键字而且在AVL树的右子树中不存在和e有相同关键字的节点则将e插入在AVL树的右子树上并且当插入之后的右子树深度增加1时分别就不同情况处理之。
AVL树的实现
1.AVL树节点的定义
templateclass K, class V
struct AVLTreeNode
{AVLTreeNodeK, V* _left;AVLTreeNodeK, V* _right;AVLTreeNodeK, V* _parent;pairK, V _kv;int _bf;AVLTreeNode(const pairK, V kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0){}
};这个C模板结构体表示一个AVL树的节点AVLTreeNodeAVL树是一种自平衡二叉搜索树。这个结构体包含了以下成员
_left指向左子节点的指针。_right指向右子节点的指针。_parent指向父节点的指针。_kv一个键值对用来存储节点的关键字和关联的值。_bf平衡因子Balance Factor用来表示节点的平衡状态。通常平衡因子是左子树的高度减去右子树的高度。AVL树要求每个节点的平衡因子在[-1, 1]范围内以保持树的平衡。
这个结构体表示了AVL树中的一个节点通常在AVL树的实现中你会有一个指向根节点的指针来访问整个树。AVL树的节点结构包括平衡因子 _bf 是为了帮助维持树的平衡当插入或删除节点时需要根据平衡因子来进行相应的旋转操作以确保树的平衡性。
2.AVL树的插入
这里我们首先定义结构体struct AVLTree
包含下面的成员:
typedef AVLTreeNodeK, V Node;
private:Node* _root nullptr;定义插入成员函数:
bool Insert(const pairK, V kv)
{if (_root nullptr){_root new Node(kv);return true;}Node* parent nullptr;Node* cur _root;while (cur){if (cur-_kv.first kv.first){parent cur;cur cur-_right;}else if (cur-_kv.first kv.first){parent cur;cur cur-_left;}else{return false;}}cur new Node(kv);if (parent-_kv.first kv.first){parent-_right cur;}else{parent-_left cur;}cur-_parent parent;// 控制平衡while (parent){if (cur parent-_right){parent-_bf;}else{parent-_bf--;}if (parent-_bf 0){break;}else if (abs(parent-_bf) 1){parent parent-_parent;cur cur-_parent;}else if (abs(parent-_bf) 2){// 说明parent所在子树已经不平衡了需要旋转处理if (parent-_bf 2 cur-_bf 1){RotateL(parent);}else if ((parent-_bf -2 cur-_bf -1)){RotateR(parent);}else if (parent-_bf -2 cur-_bf 1){RotateLR(parent);}else if (parent-_bf 2 cur-_bf -1){RotateRL(parent);}else{assert(false);}break;}else{assert(false);}}return true;
}这段代码的主要功能是往AVL树中插入一个新的键值对 kv并在插入后维护树的平衡性。下面是代码的主要步骤
如果树为空_root nullptr则直接创建一个新的根节点 _root 并插入 kv然后返回。如果树不为空进入插入节点的逻辑 使用 parent 和 cur 指针来遍历树找到应该插入的位置。如果当前节点 cur 的关键字小于 kv 的关键字则向右子树移动否则向左子树移动直到找到一个空位置插入新节点。 插入新节点后需要更新节点的父节点指针 _parent。接下来是维护树的平衡性的逻辑 在插入过程中通过循环向上更新父节点的平衡因子 _bf。如果某个节点的平衡因子为0表示它的子树高度没有变化可以停止更新平衡因子因为父节点的平衡因子也不会改变。如果某个节点的平衡因子为1或-1表示它的子树高度发生了变化需要向上继续更新平衡因子。如果某个节点的平衡因子为2或-2表示树已经不平衡了需要进行旋转操作来恢复平衡。 旋转操作的选择取决于不平衡节点及其子节点的平衡因子情况。通常AVL树有四种旋转操作分别是左旋RotateL、右旋RotateR、左右旋RotateLR和右左旋RotateRL。
平衡因子的更新规则 新增在左,parent-bf--;新增在右,parent-bf更新后,parent-bf1 or -1,说明parent插入前的平衡因子是0,说明左右子树高度相等,插入后有一边高,需要继续往上更新更新后,parent-bf0,说明parent插入前的平衡因子是1 or -1,说明左右子树一边高一边低,插入后两边一样高,插入填上了矮的那边,parent所在子树高度不变,不需要继续往上更新更新后,parent-bf2 or -2,说明parent插入前的平衡因子是1 or -1,已经平衡临界值,插入变成2 or -2,parent所在子树需要旋转处理更新后,parent-bf2 or -2,这个条件是不成立的,如果存在,则说明插入前就存在问题,需向前检查 3.AVL树的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点可能造成不平衡此时必须调整树的结构使之平衡化。根据节点插入位置的不同AVL树的旋转分为四种
3.1 新节点插入较高右子树的右侧—右右:左单旋 void RotateL(Node* parent)
{Node* subR parent-_right;Node* subRL subR-_left;parent-_right subRL;if (subRL)subRL-_parent parent;Node* ppNode parent-_parent;subR-_left parent;parent-_parent subR;if (_root parent){_root subR;subR-_parent nullptr;}else{if (ppNode-_left parent){ppNode-_left subR;}else{ppNode-_right subR;}subR-_parent ppNode;}subR-_bf parent-_bf 0;
}首先保存父节点 parent 的右子树 subR 和 subR 的左子树 subRL 的指针。将 parent 的右子树指针 _right 指向 subRL即将 subRL 作为新的 parent 的右子树。如果 subRL 存在不为 nullptr则将 subRL 的父节点指针 _parent 指向 parent以确保树的连接正确。获取 parent 的父节点指针 ppNode以确定如何连接 subR。将 subR 的左子树指针 _left 指向 parent同时将 parent 的父节点指针 _parent 指向 subR完成左旋转。如果 parent 是根节点_root parent则需要更新根节点 _root 为 subR并将 subR 的父节点指针 _parent 设置为 nullptr以确保树的根正确连接。否则如果 parent 不是根节点根据 parent 在其父节点 ppNode 中的位置将 subR 连接到正确的位置更新 subR 的父节点指针 _parent 为 ppNode。最后将 parent 和 subR 的平衡因子 _bf 设置为0因为在左旋转后它们的高度没有变化。
3.2 新节点插入较高左子树的左侧—左左:右单旋 void RotateR(Node* parent)
{Node* subL parent-_left;Node* subLR subL-_right;parent-_left subLR;if (subLR)subLR-_parent parent;Node* ppNode parent-_parent;subL-_right parent;parent-_parent subL;if (_root parent){_root subL;subL-_parent nullptr;}else{if (ppNode-_left parent){ppNode-_left subL;}else{ppNode-_right subL;}subL-_parent ppNode;}subL-_bf parent-_bf 0;
}首先保存父节点 parent 的左子树 subL 和 subL 的右子树 subLR 的指针。将 parent 的左子树指针 _left 指向 subLR即将 subLR 作为新的 parent 的左子树。如果 subLR 存在不为 nullptr则将 subLR 的父节点指针 _parent 指向 parent以确保树的连接正确。获取 parent 的父节点指针 ppNode以确定如何连接 subL。将 subL 的右子树指针 _right 指向 parent同时将 parent 的父节点指针 _parent 指向 subL完成右旋转。如果 parent 是根节点_root parent则需要更新根节点 _root 为 subL并将 subL 的父节点指针 _parent 设置为 nullptr以确保树的根正确连接。否则如果 parent 不是根节点根据 parent 在其父节点 ppNode 中的位置将 subL 连接到正确的位置更新 subL 的父节点指针 _parent 为 ppNode。最后将 parent 和 subL 的平衡因子 _bf 设置为0因为在右旋转后它们的高度没有变化。
原理同左单旋
3.3 新节点插入较高左子树的右侧—左右先左单旋再右单旋 void RotateLR(Node* parent)
{Node* subL parent-_left;Node* subLR subL-_right;int bf subLR-_bf;RotateL(parent-_left);RotateR(parent);subLR-_bf 0;if (bf 1)//上图为例,新增节点在c下方{parent-_bf 0;subL-_bf -1;}else if (bf -1)//上图为例,新增节点在b下方{parent-_bf 1;subL-_bf 0;}else if (bf 0)//上图为例,无其他子树,60为新增节点的情况{parent-_bf 0;subL-_bf 0;}else{assert(false);}
}首先保存父节点 parent 的左子树 subL 和 subL 的右子树 subLR 的指针以及 subLR 的平衡因子 bf。对 parent 的左子树 subL 进行左旋转操作以调整子树的结构。然后对 parent 进行右旋转操作以将 subL 成为 parent 的右子树。将 subLR 的平衡因子 _bf 设置为0因为它在旋转后的位置高度没有变化。根据 subLR 的平衡因子 bf 的不同值来更新节点的平衡因子 如果 bf 为1表示 subL 的左子树高度大于右子树将 parent 的平衡因子设置为0subL 的平衡因子设置为-1。如果 bf 为-1表示 subL 的右子树高度大于左子树将 parent 的平衡因子设置为1subL 的平衡因子设置为0。如果 bf 为0表示 subL 的左右子树高度相等将 parent 和 subL 的平衡因子都设置为0。 如果 bf 不是1、-1或0那么会触发 assert(false)表示出现了异常情况。
3.4 新节点插入较高右子树的左侧—右左先右单旋再左单旋 void RotateRL(Node* parent)
{Node* subR parent-_right;Node* subRL subR-_left;int bf subRL-_bf;RotateR(parent-_right);RotateL(parent);subRL-_bf 0;if (bf 1){subR-_bf 0;parent-_bf -1;}else if (bf -1){subR-_bf 1;parent-_bf 0;}else if (bf 0){parent-_bf 0;subR-_bf 0;}else{assert(false);}
}首先保存父节点 parent 的右子树 subR 和 subR 的左子树 subRL 的指针以及 subRL 的平衡因子 bf。对 parent 的右子树 subR 进行右旋转操作以调整子树的结构。然后对 parent 进行左旋转操作以将 subR 成为 parent 的左子树。将 subRL 的平衡因子 _bf 设置为0因为它在旋转后的位置高度没有变化。根据 subRL 的平衡因子 bf 的不同值来更新节点的平衡因子 如果 bf 为1表示 subRL 的左子树高度大于右子树将 subR 的平衡因子设置为0parent 的平衡因子设置为-1。如果 bf 为-1表示 subRL 的右子树高度大于左子树将 subR 的平衡因子设置为1parent 的平衡因子设置为0。如果 bf 为0表示 subRL 的左右子树高度相等将 parent 和 subR 的平衡因子都设置为0。 如果 bf 不是1、-1或0那么会触发 assert(false)表示出现了异常情况。
原理同先左单旋再右单旋
总结 假如以pParent为根的子树不平衡即pParent的平衡因子为2或者-2分以下情况考虑
pParent的平衡因子为2说明pParent的右子树高设pParent的右子树的根为pSubR
当pSubR的平衡因子为1时执行左单旋 当pSubR的平衡因子为-1时执行右左双旋
pParent的平衡因子为-2说明pParent的左子树高设pParent的左子树的根为pSubL
当pSubL的平衡因子为-1是执行右单旋 当pSubL的平衡因子为1时执行左右双旋
旋转完成后原pParent为根的子树个高度降低已经平衡不需要再向上更新。
4.AVL树的验证
4.1 验证其为二叉搜索树
中序遍历可得到一个有序的序列就说明为二叉搜索树
void InOrder()
{_InOrder(_root);cout endl;
}
private:void _InOrder(Node* root){if (root nullptr){return;}_InOrder(root-_left);cout root-_kv.first : root-_kv.second endl;_InOrder(root-_right);}首先检查当前节点 root 是否为空即树是否为空。如果为空则返回结束递归。接着递归地调用 _InOrder 函数遍历左子树 root-_left。这将按照升序访问左子树中的节点。然后输出当前节点 root 的关键字和关联的值通常使用 cout 输出到控制台。最后再次递归调用 _InOrder 函数遍历右子树 root-_right。这将按照升序访问右子树中的节点。
4.2 验证其为平衡树 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子) 节点的平衡因子是否计算正确
高度成员函数
int Height(Node* root)
{if (root nullptr)return 0;return max(Height(root-_left), Height(root-_right)) 1;
}首先检查当前节点 root 是否为空即树是否为空。如果为空则返回高度0表示空树的高度为0。如果当前节点 root 不为空那么递归地调用 Height 函数来计算左子树的高度和右子树的高度。使用 max 函数比较左子树和右子树的高度然后加上1当前节点的高度得到整棵树的高度。返回树的高度作为函数的结果。
平衡树检测函数
bool IsBalance()
{return _IsBalance(_root);
}
private:bool _IsBalance(Node* root){if (root nullptr){return true;}int leftHT Height(root-_left);int rightHT Height(root-_right);int diff rightHT - leftHT;if (diff ! root-_bf){cout root-_kv.first 平衡因子异常 endl;return false;}return abs(diff) 2 _IsBalance(root-_left) _IsBalance(root-_right);}首先检查当前节点 root 是否为空即树是否为空。如果为空则返回 true因为空树是平衡的。如果当前节点 root 不为空那么首先计算左子树和右子树的高度分别存储在 leftHT 和 rightHT 中。然后计算左子树和右子树高度差右子树高度减去左子树高度存储在 diff 变量中。检查当前节点的平衡因子 _bf 是否等于 diff如果不相等表示平衡因子异常输出错误信息并返回 false。继续检查当前节点是否满足AVL树的平衡条件即平衡因子的绝对值不超过1以及递归检查左子树和右子树是否也是平衡的调用 _IsBalance 函数。如果所有条件都满足返回 true 表示当前子树是平衡的。
4.3 验证用例
常规场景1
{16, 3, 7, 11, 9, 26, 18, 14, 15}特殊场景2
{4, 2, 6, 1, 3, 5, 15, 7, 16, 14} AVL树实现及验证所有代码
1.AVL代码实现
AVL.hpp
#pragma once
#include iostream
#include algorithm
#include assert.h
using namespace std;
templateclass K, class V
struct AVLTreeNode
{AVLTreeNodeK, V* _left;AVLTreeNodeK, V* _right;AVLTreeNodeK, V* _parent;pairK, V _kv;int _bf;AVLTreeNode(const pairK, V kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0){}
};templateclass K, class V
struct AVLTree
{typedef AVLTreeNodeK, V Node;
public:bool Insert(const pairK, V kv){if (_root nullptr){_root new Node(kv);return true;}Node* parent nullptr;Node* cur _root;while (cur){if (cur-_kv.first kv.first){parent cur;cur cur-_right;}else if (cur-_kv.first kv.first){parent cur;cur cur-_left;}else{return false;}}cur new Node(kv);if (parent-_kv.first kv.first){parent-_right cur;}else{parent-_left cur;}cur-_parent parent;while (parent){if (cur parent-_right){parent-_bf;}else{parent-_bf--;}if (parent-_bf 0){break;}else if (abs(parent-_bf) 1){parent parent-_parent;cur cur-_parent;}else if (abs(parent-_bf) 2){if (parent-_bf 2 cur-_bf 1){RotateL(parent);}else if ((parent-_bf -2 cur-_bf -1)){RotateR(parent);}else if (parent-_bf -2 cur-_bf 1){RotateLR(parent);}else if (parent-_bf 2 cur-_bf -1){RotateRL(parent);}else{assert(false);}break;}else{assert(false);}}return true;}void InOrder(){_InOrder(_root);cout endl;}bool IsBalance(){return _IsBalance(_root);}private:int BalanceFactor(Node* node){if (node nullptr){return 0;}return Height(node-_left) - Height(node-_right);}bool _IsBalance(Node* root){if (root nullptr){return true;}int leftHT Height(root-_left);int rightHT Height(root-_right);int diff rightHT - leftHT;if (diff ! root-_bf){cout root-_kv.first 平衡因子异常 endl;return false;}return abs(diff) 2 _IsBalance(root-_left) _IsBalance(root-_right);}int Height(Node* root){if (root nullptr)return 0;return max(Height(root-_left), Height(root-_right)) 1;}void RotateL(Node* parent){Node* subR parent-_right;Node* subRL subR-_left;parent-_right subRL;if (subRL)subRL-_parent parent;Node* ppNode parent-_parent;subR-_left parent;parent-_parent subR;if (_root parent){_root subR;subR-_parent nullptr;}else{if (ppNode-_left parent){ppNode-_left subR;}else{ppNode-_right subR;}subR-_parent ppNode;}subR-_bf parent-_bf 0;}void RotateR(Node* parent){Node* subL parent-_left;Node* subLR subL-_right;parent-_left subLR;if (subLR)subLR-_parent parent;Node* ppNode parent-_parent;subL-_right parent;parent-_parent subL;if (_root parent){_root subL;subL-_parent nullptr;}else{if (ppNode-_left parent){ppNode-_left subL;}else{ppNode-_right subL;}subL-_parent ppNode;}subL-_bf parent-_bf 0;}void RotateLR(Node* parent){Node* subL parent-_left;Node* subLR subL-_right;int bf subLR-_bf;RotateL(parent-_left);RotateR(parent);subLR-_bf 0;if (bf 1){parent-_bf 0;subL-_bf -1;}else if (bf -1){parent-_bf 1;subL-_bf 0;}else if (bf 0){parent-_bf 0;subL-_bf 0;}else{assert(false);}}void RotateRL(Node* parent){Node* subR parent-_right;Node* subRL subR-_left;int bf subRL-_bf;RotateR(parent-_right);RotateL(parent);subRL-_bf 0;if (bf 1){subR-_bf 0;parent-_bf -1;}else if (bf -1){subR-_bf 1;parent-_bf 0;}else if (bf 0){parent-_bf 0;subR-_bf 0;}else{assert(false);}}void _InOrder(Node* root){if (root nullptr){return;}_InOrder(root-_left);cout root-_kv.first : root-_kv.second endl;_InOrder(root-_right);}
private:Node* _root nullptr;
};2.AVL验证代码实现
AVLTEST.cpp
#include AVL.hpp
int main()
{//int a[]{16, 3, 7, 11, 9, 26, 18, 14, 15};int a[] { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTreeint, int avl1;for (auto e : a)avl1.Insert(make_pair(e, e));avl1.InOrder();cout IsBlance: avl1.IsBalance() endl;return 0;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/86203.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!