AVL树就是 可以 绝对保证 平衡的 搜索树,怎么才算平衡呢?
如何控制 平衡呢?我这里实现用的是平衡因子。也就是说 每个节点都有平衡因子。
平衡因子规则:
这就能控制平衡了?当然不能,平衡因子是用于 判断 需不需要旋转 的依据。对,是通过旋转来控制平衡的。旋转有 左单旋,右单旋 ,左右双旋,右左双旋转。听起来复杂,没事 会 理清楚的。
插入节点 平衡因子更新规则:
a,b,c 都是 子树,h代表树的高度, 3 和 6 的平衡因子给出了已经,就是 右子树高度 - 左子树高度。
那么我有个问题:在哪里插入一个节点会 引发 左单旋 ?
所以 左单旋 的平衡因子条件:
口诀: 右边单纯高,左单旋
右子树
(6节点) 变为 它原来的右子树
(6节点)的左子树
(b子树)。就这样 完成 左单旋,但是有人会问:你怎么知道?上面的 6节点的 左子树可以 做3节点的 右子树?还有 3节点 可以作为 6节点的 左子树。
这就是 二叉搜索树呀。
总结: 右边单纯高,左单旋,本质就是 这颗树整体的高度 减一,去弥补 左子树。再直白点就是 让出问题的节点 下去,填补左子树,从而达到平衡。
其实 就是 上面的例子反一下,不会讲的很细。但是 逻辑还在。
很明显,6节点 的平衡因子为 -2,需要旋转。
所以 右单旋 的平衡因子条件:
口诀: 左边单纯高,右单旋
左子树
(3节点) 变为 它原来的左子树
(3节点)的右子树
(b子树)。左子树
(3节点) 的右子树。图解:
这就是 右单选。
这个其实用 具体点的图 好讲,因为 它得先右旋一下,再左旋一下,比较复杂,所以 我给出三种具像图:
1. h 为 0,也就是 高度为 0。
(1)先对 平衡因子 为-1 的节点 (6节点) ,进行右单旋
可以看到,先在是什么情况?平衡因子出问题的是 2,它的右子树平衡因子是1。单纯的右边高,所以要怎么办?左单旋。
(2) 再对 平衡因子 为 2 的节点 ,进行左单旋
2. h = 1的情况
那么现在 新插入的节点 插入到 5节点 的左边或者右边 都会造成 右左双旋
那么 对 6节点先进行右单旋:
3. 当 h=2 的情况:
这种情况就多了,但是我只以一种为例子:
总结一下:我们 抽像的想一下
这个左右双旋,无非就是将 b这颗子树 的根节点 推上去 成为 3节点和6节点 的根,然后 将 b子树 的左子树 分给 3节点,右子树 分给 6节点。就类似这样:
说说结论:先对 出现问题的节点的右子树 进行 右单旋,再对出现问题的节点 进左单旋。当然 平衡因子这里有细节,实现的时候,我们再讲。
图解如下:
(1) 先 对3节点 进行 左单旋
就是这样,完成了 左右双旋。
什么时候需要旋转?当出现 平衡因子 为 2或者 -2 时。
就这些情况,上面 一 一 对应的 都讲到了,只是理论实现,我们下面进行实践。
template<class K, class V>
struct AVL_node
{
AVL_node<K, V>* left_;
AVL_node<K, V>* right_;
AVL_node<K, V>* parents_;
std::pair<K, V> kv_;
int ef_; //Equilibrium factor 平衡因子
AVL_node(const std::pair<K, V>& kv)
:left_(nullptr),
right_(nullptr),
parents_(nullptr),
kv_(kv),
ef_(0)
{}
};
用三叉链来 实现 AVL树:
有指向左树的指针,右树的指针;指向父亲的指针;它的节点数据 是一个 pair
AVL树 只需要记录根节点,就可以了。构造也很简单。
template<class K,class V>
class AVL_tree
{
typedef AVL_node<K,V> Node;
private:
Node* _root;
public:
AVL_tree()
:_root(nullptr)
{}
};
这是重点,首先 AVL树是一个搜索树,所以得遵循搜索树的逻辑去 插入节点 ;其次 我们要更新平衡因子;然后根据 平衡因子 来判断 需不要 旋转。对于平衡因子的更新,以及 是否要旋转 ,上文都有讲解。
public:
bool insert(const std::pair<K,V>& node)
{
if (_root == nullptr)
{
_root = new Node(node);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (node.first > cur->kv_.first)
{
parent = cur;
cur = cur->right_;
}
else if (node.first< cur->kv_.first)
{
parent = cur;
cur = cur->left_;
}
else
{
assert(false);
}
}
cur = new Node(node);
if (parent->kv_.first > cur->kv_.first)
{
parent->left_ = cur;
cur->parents_ = parent;
}
else
{
parent->right_ = cur;
cur->parents_ = parent;
}
/ 控制平衡
更新平衡因子
while (parent)
{
if (cur == parent->left_)
{
parent->ef_--;
}
else
{
parent->ef_++;
}
if (parent->ef_ == 0)
break;
else if (parent->ef_ == 1 || parent->ef_ == -1)
{
继续往上更新 平衡因子
cur = parent;
parent = cur->parents_;
}
else if (parent->ef_ == 2 || parent->ef_ == -2)
{
// 旋转
if (parent->ef_ == 2 && parent->right_->ef_ == 1)
{
// 左单旋转
revolov_L(parent);
}
else if (parent->ef_ == 2 && parent->right_->ef_ == -1)
{
/// 右左双旋
revolov_RL(parent);
}
else if (parent->ef_ == -2 && parent->left_->ef_ == -1)
{
右单旋
revolov_R(parent);
}
else if (parent->ef_ == -2 && parent->right_->ef_ == 1)
{
/// 左右双旋
revolov_LR(parent);
}
else
{
assert(false);
}
break;
}
}
}
issue 就是 问题,英文翻译一锤。还有那个 issue_node_parent 有三种情况:
这种情况 就是 左单旋:
void revolov_L(Node* issue_node)
{
Node* issue_Rnode = issue_node->right_;
Node* issue_Rnode_L = issue_Rnode->left_;
Node* issue_node_parent = issue_node->parents_;
issue_node->right_ = issue_Rnode_L;
if (issue_Rnode_L)
{
issue_Rnode_L->parents_ = issue_node;
}
issue_Rnode->left_ = issue_node;
issue_node->parents_ = issue_Rnode;
if (issue_node_parent == nullptr)
{
_root = issue_Rnode;
issue_Rnode->parents_ = nullptr;
}
else
{
if (issue_node_parent->left_ == issue_node)
issue_node_parent->left_ = issue_Rnode;
else
issue_node_parent->right_ = issue_Rnode;
issue_Rnode->parents_ = issue_node_parent;
}
issue_node->ef_ = issue_Rnode->ef_ = 0;
}
其实 旋转的关键 就是在于 改变 三叉链的 链接关系。
代码结合 图解:
(1)
issue_node->right_ = issue_Rnode_L;
// 判读是否为空
if (issue_Rnode_L)
{
issue_Rnode_L->parents_ = issue_node;
}
issue_Rnode->left_ = issue_node;
issue_node->parents_ = issue_Rnode;
// 改变 issue_Rnode 的 parents
if (issue_node_parent == nullptr)
{
_root = issue_Rnode;
issue_Rnode->parents_ = nullptr;
}
else
{
if (issue_node_parent->left_ == issue_node)
issue_node_parent->left_ = issue_Rnode;
else
issue_node_parent->right_ = issue_Rnode;
issue_Rnode->parents_ = issue_node_parent;
}
void revolov_R(Node* issue_node)
{
Node* issue_Lnode = issue_node->left_;
Node* issue_Lnode_R = issue_Lnode->right_;
Node* issue_node_parent = issue_node->parents_;
issue_node->left_ = issue_Lnode_R;
if (issue_Lnode_R)
{
issue_Lnode_R->parents_ = issue_node;
}
issue_Lnode->right_ = issue_node;
issue_node->parents_ = issue_Lnode;
if (issue_node_parent == nullptr)
{
_root = issue_Lnode;
issue_Lnode->parents_ = nullptr;
}
else
{
if (issue_node_parent->left_ == issue_node)
issue_node_parent->left_ = issue_Lnode;
else
issue_node_parent->right_ = issue_Lnode;
issue_Lnode->parents_ = issue_node_parent;
}
issue_node->ef_ = issue_Lnode->ef_ = 0;
}
这就是 和上面 反一下,逻辑一样的。
void revolov_RL(Node* issue_node)
{
revolov_R(issue_node->right_);
revolov_L(issue_node);
}
void revolov_LR(Node* issue_node)
{
revolov_L(issue_node->left_);
revolov_R(issue_node);
}
我们都知道 搜索树 的中序遍历就是 有序的。所以 可以用来 先验证 一下 我们上面 实现的 AVL树是否为 搜索树。
public:
void InOrder()
{
_InOrder(_root);
}
private:
void _InOrder(Node* root)
{
if (root == NULL)
return;
_InOrder(root->left_);
std::cout << root->kv_.first << ":" << root->kv_.second << std::endl;
_InOrder(root->right_);
}
好,现在 我们 来验证一下:
int main()
{
AVL_tree<int, int> t;
//int a[] = {5,4,3,2,1,0};
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
t.insert(std::make_pair(e, e));
}
t.InOrder();
return 0;
}
运行结果:
验证平衡的关键 就是 :左右子树的差值 ,是否 与平衡因子 相等。
有人说:直接 查看 平衡因子就行了,这是不可以的,因为平衡因子 是由我们来控制的,它不一定能 反馈真实情况。
public:
bool IsBalance()
{
return _IsBalance(_root);
}
private:
int Height(Node* root)
{
if (root == NULL)
return 0;
int leftHeight = Height(root->left_);
int rightHeight = Height(root->right_);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalance(Node* root)
{
if (root == NULL)
return true;
// 对当前树进行检查
int leftHeight = Height(root->left_);
int rightHeight = Height(root->right_);
if (rightHeight - leftHeight != root->ef_)
{
std:: cout << root->kv_.first << "现在是:" << root->ef_ << std::endl;
std:: cout << root->kv_.first << "应该是:" << rightHeight - leftHeight << std::endl;
return false;
}
return abs(rightHeight - leftHeight) < 2
&& _IsBalance(root->left_)
&& _IsBalance(root->right_);
}
好,验证一下是否平衡:
int main()
{
AVL_tree<int, int> t;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
t.insert(std::make_pair(e, e));
std::cout << "Insert" << e << ":" << t.IsBalance() << std::endl;
}
return 0;
}
发现 插入 14 的时候,导致 6节点 的平衡因子 出错了,平衡因子有问题 绝对会影响 后序的插入。
这是为什么呢?
6节点 的平衡因子 是 -1,但是 因为 对它进行了 左单旋,将它的平衡因子 置成了 0。这就是问题所在,所以双旋需要控制平衡因子,怎么控制呢?
旋转完后, 6 的平衡因子 有两种情况 :
总结一下:出问题的节点 双旋完之后,它的平衡因子 必然为0;关键就是 那个平衡因子为 -1的节点,双旋完后,它的平衡因子需要控制,它的平衡因子控制看的是 b树的情况。
我还是 画画 图吧。
(1) 第一种情况,这个比较简单
从这其实 也能再次抽象 理解 :
将 那个新增节点的根节点,推上去作为此支树的根,然后让出问题的节点和问题节点的右树瓜分它的 左子树和右子树。 没瓜分到的 所以就会 导致 平衡因子 为 1或者 -1。
代码实现:
void revolov_RL(Node* issue_node)
{
Node* issue_Rnode = issue_node->right_;
Node* issue_Rnode_L = issue_Rnode->left_;
int flag = 0;
if (issue_Rnode_L->ef_ == 1)
{
flag = 1;
}
if (issue_Rnode_L->ef_ == -1)
{
flag = -1;
}
revolov_R(issue_Rnode);
revolov_L(issue_node);
if (flag == 1)
{
issue_node->ef_ = -1;
}
if (flag == -1)
{
issue_Rnode->ef_ = 1;
}
}
有了上面的基础,其实 左右双旋也是可以实现的,主要是 看 b树情况:
只不过就反一下而已,我直接画图:
代码实现:
void revolov_LR(Node* issue_node)
{
Node* issue_Lnode = issue_node->left_;
Node* issue_Lnode_R = issue_Lnode->right_;
int flag = 0;
if (issue_Lnode_R->ef_ == 1)
{
flag = 1;
}
if (issue_Lnode_R->ef_ == -1)
{
flag = -1;
}
revolov_L(issue_node->left_);
revolov_R(issue_node);
if (flag == 1)
{
issue_Lnode->ef_ = -1;
}
if (flag == -1)
{
issue_node->ef_ = 1;
}
}
就是这样,大家下去好好思考。
#pragma once
#include
#include
namespace AVL
{
template<class K, class V>
struct AVL_node
{
AVL_node<K, V>* left_;
AVL_node<K, V>* right_;
AVL_node<K, V>* parents_;
std::pair<K, V> kv_;
int ef_; //Equilibrium factor 平衡因子
AVL_node(const std::pair<K, V>& kv)
:left_(nullptr),
right_(nullptr),
parents_(nullptr),
kv_(kv),
ef_(0)
{}
};
template<class K,class V>
class AVL_tree
{
typedef AVL_node<K,V> Node;
private:
Node* _root;
public:
AVL_tree()
:_root(nullptr)
{}
private:
void revolov_L(Node* issue_node)
{
Node* issue_Rnode = issue_node->right_;
Node* issue_Rnode_L = issue_Rnode->left_;
Node* issue_node_parent = issue_node->parents_;
issue_node->right_ = issue_Rnode_L;
if (issue_Rnode_L)
{
issue_Rnode_L->parents_ = issue_node;
}
issue_Rnode->left_ = issue_node;
issue_node->parents_ = issue_Rnode;
if (issue_node_parent == nullptr)
{
_root = issue_Rnode;
issue_Rnode->parents_ = nullptr;
}
else
{
if (issue_node_parent->left_ == issue_node)
issue_node_parent->left_ = issue_Rnode;
else
issue_node_parent->right_ = issue_Rnode;
issue_Rnode->parents_ = issue_node_parent;
}
issue_node->ef_ = issue_Rnode->ef_ = 0;
}
void revolov_R(Node* issue_node)
{
Node* issue_Lnode = issue_node->left_;
Node* issue_Lnode_R = issue_Lnode->right_;
Node* issue_node_parent = issue_node->parents_;
issue_node->left_ = issue_Lnode_R;
if (issue_Lnode_R)
{
issue_Lnode_R->parents_ = issue_node;
}
issue_Lnode->right_ = issue_node;
issue_node->parents_ = issue_Lnode;
if (issue_node_parent == nullptr)
{
_root = issue_Lnode;
issue_Lnode->parents_ = nullptr;
}
else
{
if (issue_node_parent->left_ == issue_node)
issue_node_parent->left_ = issue_Lnode;
else
issue_node_parent->right_ = issue_Lnode;
issue_Lnode->parents_ = issue_node_parent;
}
issue_node->ef_ = issue_Lnode->ef_ = 0;
}
void revolov_RL(Node* issue_node)
{
Node* issue_Rnode = issue_node->right_;
Node* issue_Rnode_L = issue_Rnode->left_;
int flag = 0;
if (issue_Rnode_L->ef_ == 1)
{
flag = 1;
}
if (issue_Rnode_L->ef_ == -1)
{
flag = -1;
}
revolov_R(issue_Rnode);
revolov_L(issue_node);
if (flag == 1)
{
issue_node->ef_ = -1;
}
if (flag == -1)
{
issue_Rnode->ef_ = 1;
}
}
void revolov_LR(Node* issue_node)
{
Node* issue_Lnode = issue_node->left_;
Node* issue_Lnode_R = issue_Lnode->right_;
int flag = 0;
if (issue_Lnode_R->ef_ == 1)
{
flag = 1;
}
if (issue_Lnode_R->ef_ == -1)
{
flag = -1;
}
revolov_L(issue_node->left_);
revolov_R(issue_node);
if (flag == 1)
{
issue_Lnode->ef_ = -1;
}
if (flag == -1)
{
issue_node->ef_ = 1;
}
}
public:
bool insert(const std::pair<K,V>& node)
{
if (_root == nullptr)
{
_root = new Node(node);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (node.first > cur->kv_.first)
{
parent = cur;
cur = cur->right_;
}
else if (node.first< cur->kv_.first)
{
parent = cur;
cur = cur->left_;
}
else
{
assert(false);
}
}
cur = new Node(node);
if (parent->kv_.first > cur->kv_.first)
{
parent->left_ = cur;
cur->parents_ = parent;
}
else
{
parent->right_ = cur;
cur->parents_ = parent;
}
/ 控制平衡
更新平衡因子
while (parent)
{
if (cur == parent->left_)
{
parent->ef_--;
}
else
{
parent->ef_++;
}
if (parent->ef_ == 0)
break;
else if (parent->ef_ == 1 || parent->ef_ == -1)
{
继续往上更新 平衡因子
cur = parent;
parent = cur->parents_;
}
else if (parent->ef_ == 2 || parent->ef_ == -2)
{
// 旋转
if (parent->ef_ == 2 && parent->right_->ef_ == 1)
{
// 左单旋转
revolov_L(parent);
}
else if (parent->ef_ == 2 && parent->right_->ef_ == -1)
{
/// 右左双旋
revolov_RL(parent);
}
else if (parent->ef_ == -2 && parent->left_->ef_ == -1)
{
右单旋
revolov_R(parent);
}
else if (parent->ef_ == -2 && parent->right_->ef_ == 1)
{
/// 左右双旋
revolov_LR(parent);
}
else
{
assert(false);
}
break;
}
}
}
public:
void InOrder()
{
_InOrder(_root);
}
private:
void _InOrder(Node* root)
{
if (root == NULL)
return;
_InOrder(root->left_);
std::cout << root->kv_.first << ":" << root->kv_.second << std::endl;
_InOrder(root->right_);
}
public:
bool IsBalance()
{
return _IsBalance(_root);
}
private:
int Height(Node* root)
{
if (root == NULL)
return 0;
int leftHeight = Height(root->left_);
int rightHeight = Height(root->right_);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalance(Node* root)
{
if (root == NULL)
return true;
// 对当前树进行检查
int leftHeight = Height(root->left_);
int rightHeight = Height(root->right_);
if (rightHeight - leftHeight != root->ef_)
{
std:: cout << root->kv_.first << "现在是:" << root->ef_ << std::endl;
std:: cout << root->kv_.first << "应该是:" << rightHeight - leftHeight << std::endl;
return false;
}
return abs(rightHeight - leftHeight) < 2
&& _IsBalance(root->left_)
&& _IsBalance(root->right_);
}
};
}
以上就是 AVL树 的实现,有问题 的私信,或者评论。