前言
众所周知红黑树是由AVL
树改进得来的,想要深入学习哈希表的底层存储那么AVL
的学习就相当有必要了。
本来想将AVL
的插入删除都能实现,但是在写删除功能时碰到了难题和Bug,所以暂时先给出插入的实现过程,和删除功能的实现思路
一、插入
1.结构
相比树节点多出两个值:
parent
记录父节点blance
记录平衡因子
其中平衡因子代表左子树和右子树的最大深度差- 比如
blance == 0
代表两子树的最大深度相等
blance == 1
代表右子树最大深度值 比 左子树最大深度值 大一层
blance == -1
代表右子树最大深度值 比 左子树最大深度值 小一层
树的结构则只保存根节点即可
template<class T>
struct AVLNode
{
T value;
AVLNode* left;
AVLNode* right;
AVLNode* parent;
int blance;
AVLNode(const T& _value)
{
value = _value;
left = nullptr;
right = nullptr;
parent = nullptr;
blance = 0;
}
};
template<class T>
class AVLTree
{
typedef AVLNode<T> Node;
Node* root = nullptr;
}
2.如何实现插入
众所周知,AVL
是一棵二叉搜索树,那么这就意味着,在整个树的任意节点处,都存在:
- 左子树任意值小于本节点,右子树任意节点值大于本节点(已存在的值不插入)
- 由于上述性质,显而易见我们可以通过判断value值大小的确定插入位置,从根节点开始,层层向下查找
void insert(const T& value)
{
Node* node = new Node(value);
if (root == nullptr)
{
root = node;
return;
}
Node* parent = nullptr;
Node* cur = root;
while (cur)
{
parent = cur;
if (cur->value < node->value)
//较之较大则向右
cur = cur->right;
else if (cur->value > node->value)
//较之较小则向左
cur = cur->left;
else//已经相等则不插入
{
delete node;
return;
}
}
node->parent = parent;
if (parent->value > node->value)
parent->left = node;
else
parent->right = node;
changeBalanceInsert(node);
}
3.左右旋转
由二叉树的性质可知,当你插入的值是有序排列时,搜索二叉树的查找性能将会退化成为链表的O(N)
级别,为了解决这个问题,AVL
树应运而生。
AVL
的最终要的两个性质分别为:中序遍历有序(搜索二叉树)、平衡。
平横是什么呢?
平衡即为:每个节点的两个子树最大深度差绝对值不超过1
例如:下图中标注了所有平衡因子值
不平衡:
所有节点中,只要有一个节点失衡,那么该树就不为平衡二叉树
为了保持平衡,AVL
引出了左右旋转的概念
- 左单旋
- 右单旋
由图可知,每次旋转就是切换一下节点位置和其子节点,做法很简单,共三部
- 先将所有可能切换的节点记录下来,例如图中的
E
,S
,between E&S
,还有旋转前需改变指向的父节点 - 分别更改这三个节点的链接位置
- 更新可能改变的平衡因子值
//右单旋
void rotateR(Node* node)
{
Node* parNode = node->parent;
Node* leftNode = node->left;
Node* leftRNode = node->left->right;
// 切换目标节点左节点的右节点
if (leftRNode)
leftRNode->parent = node;
node->left = leftRNode;
// 切换目标节点左节点位置
node->parent = leftNode;
leftNode->right = node;
leftNode->parent = parNode;
if (node == root)
//是根节点,无父节点
root = leftNode;
else if (node == parNode->left)
//是父节点的左孩子
parNode->left = leftNode;
else
//是父节点的右孩子
parNode->right = leftNode;
//调整平衡因子
node->blance = 0;
leftNode->blance = 0;
}
//左单旋
void rotateL(Node* node)
{
Node* parNode = node->parent;
Node* rightNode = node->right;
Node* rightLNode = node->right->left;
// 切换目标节点右节点的左节点
if (rightLNode)
rightLNode->parent = node;
node->right = rightLNode;
// 切换目标节点右节点位置
node->parent = rightNode;
rightNode->left = node;
rightNode->parent = parNode;
// 父节点信息更改
if (node == root)
//是根节点
root = rightNode;
else if (node == parNode->left)
//是父节点的左孩子
parNode->left = rightNode;
else
//是父节点的右孩子
parNode->right = rightNode;
//调整平衡因子
node->blance = 0;
rightNode->blance = 0;
}
4.双旋
仅靠单旋无法解决所有问题,例如下面
为了保持平衡,AVL
使用的是双旋的解决办法,先将需要旋转的节点的子节点进行旋转调整,再对本节点进行旋转
先将根节点的右孩子右单旋(先不调整平衡因子)
再将根节点左单旋
这种旋转方式为右左双旋,即孩子先向右单旋,再对自己左单旋
左右双旋也是同理
//左右双旋
void rotateLR(Node* node)
{
Node* leftNode = node->left;
Node* leftRNode = node->left->right;
int lRBf = leftRNode->blance;
rotateL(leftNode);
rotateR(node);
node->blance = 0;
leftNode->blance = 0;
leftRNode->blance = 0;
//更改平衡因子
if (lRBf == 1)
leftNode->blance = -1;
else if (lRBf == -1)
node->blance = 1;
}
//右左双旋
void rotateRL(Node* node)
{
Node* rightNode = node->right;
Node* rightLNode = node->right->left;
int rLBf = rightLNode->blance;
rotateR(rightNode);
rotateL(node);
node->blance = 0;
rightNode->blance = 0;
rightLNode->blance = 0;
//更改平衡因子
if (rLBf == 1)
node->blance = -1;
else if (rLBf == -1)
rightNode->blance = 1;
}
代码
插入完整代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
template<class T>
struct AVLNode
{
T value;
AVLNode* left;
AVLNode* right;
AVLNode* parent;
int blance;
AVLNode(const T& _value)
{
value = _value;
left = nullptr;
right = nullptr;
parent = nullptr;
blance = 0;
}
};
template<class T>
class AVLTree
{
typedef AVLNode<T> Node;
Node* root = nullptr;
//右单旋
void rotateR(Node* node)
{
Node* parNode = node->parent;
Node* leftNode = node->left;
Node* leftRNode = node->left->right;
// 切换目标节点左节点的右节点
if (leftRNode)
leftRNode->parent = node;
node->left = leftRNode;
// 切换目标节点左节点位置
node->parent = leftNode;
leftNode->right = node;
leftNode->parent = parNode;
if (node == root)
//是根节点
root = leftNode;
else if (node == parNode->left)
//是父节点的左孩子
parNode->left = leftNode;
else
//是父节点的右孩子
parNode->right = leftNode;
//调整平衡因子
node->blance = 0;
leftNode->blance = 0;
}
//左单旋
void rotateL(Node* node)
{
Node* parNode = node->parent;
Node* rightNode = node->right;
Node* rightLNode = node->right->left;
// 切换目标节点右节点的左节点
if (rightLNode)
rightLNode->parent = node;
node->right = rightLNode;
// 切换目标节点右节点位置
node->parent = rightNode;
rightNode->left = node;
rightNode->parent = parNode;
// 父节点信息更改
if (node == root)
//是根节点
root = rightNode;
else if (node == parNode->left)
//是父节点的左孩子
parNode->left = rightNode;
else
//是父节点的右孩子
parNode->right = rightNode;
//调整平衡因子
node->blance = 0;
rightNode->blance = 0;
}
//左右双旋
void rotateLR(Node* node)
{
Node* leftNode = node->left;
Node* leftRNode = node->left->right;
int lRBf = leftRNode->blance;
rotateL(leftNode);
rotateR(node);
node->blance = 0;
leftNode->blance = 0;
leftRNode->blance = 0;
//更改平衡因子
if (lRBf == 1)
leftNode->blance = -1;
else if (lRBf == -1)
node->blance = 1;
}
//右左双旋
void rotateRL(Node* node)
{
Node* rightNode = node->right;
Node* rightLNode = node->right->left;
int rLBf = rightLNode->blance;
rotateR(rightNode);
rotateL(node);
node->blance = 0;
rightNode->blance = 0;
rightLNode->blance = 0;
//更改平衡因子
if (rLBf == 1)
node->blance = -1;
else if (rLBf == -1)
rightNode->blance = 1;
}
//插入时更改平衡性
void changeBalanceInsert(Node* cur)
{
Node* parent = cur->parent;
while (cur->parent)
{
parent = cur->parent;
//更新平衡因子
parent->blance += cur == parent->left ? -1 : 1;
//平衡时不再向上
if (parent->blance == 0)
break;
else if (parent->blance == 1 || parent->blance == -1)
cur = parent;
// 需要旋转
else if (parent->blance == -2)
{
if (parent->left->blance == -1)//右单旋
rotateR(parent);
else//左右单旋
rotateLR(parent);
break;
}
else if (parent->blance == 2)
{
if (parent->right->blance == 1)//左单旋
rotateL(parent);
else//右左单旋
rotateRL(parent);
break;
}
}
}
public:
void insert(const T& value)
{
Node* node = new Node(value);
if (root == nullptr)
{
root = node;
return;
}
Node* parent = nullptr;
Node* cur = root;
while (cur)
{
parent = cur;
if (cur->value < node->value)
//较之较大则向右
cur = cur->right;
else if (cur->value > node->value)
//较之较小则向左
cur = cur->left;
else//已经相等则不插入
return;
}
node->parent = parent;
if (parent->value > node->value)
parent->left = node;
else
parent->right = node;
changeBalanceInsert(node);
}
//下面的都是测试方法
//获取最大深度
int getDepth(Node* node)
{
if (node == nullptr) return 0;
return max(getDepth(node->left), getDepth(node->right)) + 1;
}
//打印整棵树
void printAll()
{
if (root == nullptr) return;
int depth = getDepth(root);
vector<vector<Node*>> container(depth);
//遍历存储
for (int i = 0; i < depth; ++i)
{
if (i == 0)
{
container[0].push_back(root);
continue;
}
for (Node* ele : container[i - 1])
{
if (ele == nullptr)
{
container[i].push_back(nullptr);
container[i].push_back(nullptr);
}
else
{
container[i].push_back(ele->left);
container[i].push_back(ele->right);
}
}
}
system("cls");
//打印
for (int i = 0; i < depth; ++i)
{
int sp = (1 << (depth - 1 - i)) + (container[depth - 1].size() / container[i].size() - 1);
separator(sp);
for (int j = 0; j < container[i].size(); ++j)
{
if(j != 0)
separator(2 * sp);
if (container[i][j])
{
cout << container[i][j]->value;
if (container[i][j]->value < 10)
separator(1);
}
else
{
separator(2);
}
}
cout << endl;
}
}
//打印间隔
void separator(int num)
{
for (int i = 0; i < num; ++i)
cout << ' ';
}
}
二、删除
删除的基本思路是,如果被删除节点有孩子,则将其不断向下交换,直到交换到没有孩子为止
如果没有孩子,则可以将其直接删除,再向上调整整棵树。