前面 二叉搜索树
的时间复杂度那里提过一嘴 AVL树 和 红黑树
. 因为二叉搜索树的时间复杂度是 O(高度次), 最坏情况下 -- 退化到单支链, 是 O(N)
; AVL 和 红黑树
可以避免这种 极端情况, 时间复杂度是 O(log N)
️AVL树是如何做到避免 二叉搜索树的极端情况的呢?
三叉链 && 平衡因子
至于它们到底是什么, 有什么妙用, 下面会有详细的解释
AVL树具有以下的特点:
高度差(平衡因子)
的绝对值不超过 1 (-1, 0 1)
️为啥不让每棵子树的 平衡因子的绝对值为 0 ?
template<class K, class V>
struct AVLTreeNode
{
public:
AVLTreeNode(const pair<K,V>& kv)
:_kv(kv)
{}
public:
pair<K, V> _kv;
AVLTreeNode<K, V>* _left = nullptr;
AVLTreeNode<K, V>* _right = nullptr;
AVLTreeNode<K, V>* _parent = nullptr;
int _bf = 0;
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(nullptr)
{}
private:
// 根节点
Node* _root = nullptr;
// 记录旋转次数
int RotateCount = 0;
};
二叉树的插入逻辑 + 更新平衡因子
bool Insert(const pair<K, V>& kv)
{
//二叉搜索树的插入逻辑
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = _root;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// 链接
cur = new Node(kv);
if (cur->_kv.first > parent->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
parent = cur->_parent;
// 更新平衡因子
// ... ...
}
那重点就是 如何更新平衡因子
:
首先, 先明确; 新插入的节点不影响自己的平衡因子, 只会影响父亲节点到 root这一段的节点的平衡因子
其次, 要讨论插入节点的位置
最后, 也要讨论插入后的parent的平衡因子
这里有几个问题:
️为什么更新后的parent的平衡因子等于 1 或 -1, 要继续往上更新?
parent的平衡因子为 0 或 更新到了root
️ 为什么更新后的parent等于空, 也要停止更新呢?
只有root 的父亲节点才是 空
更新到root节点
️ 为什么旋转后就停止更新了呢?
旋转逻辑
来进行讲解insert的主体结构
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = _root;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// 链接
cur = new Node(kv);
if (cur->_kv.first > parent->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
parent = cur->_parent;
// 更新平衡因子
while (parent) // 最差更新到root节点
{
// 1. 先更新一下parent
// 新插在右
if (parent->_right == cur)
{
parent->_bf++;
}
else // 新插在左
{
parent->_bf--;
}
// 2. 检查更新后的parent是否影响平衡
if (parent->_bf == 0)
{
// 停止更新平衡因子
break;
}
else if(parent->_bf == 1 || parent->_bf == -1)
{
// 继续向上更新
cur = parent;
parent = parent->_parent;
}
// 需要旋转, 来保持平衡
else if (parent->_bf == 2 || parent->_bf == -2)
{
// 旋转的逻辑
// ...
// ...
// 旋转后停止更新平衡因子
break;
}
}
return true;
}
原本的树形结构符合AVL树的特点, 如果插入一个新节点 造成不平衡了, 即parent的平衡因子 等于 2 或 -2了, 这时候就要进行旋转. 根据 插入节点的位置
, 一共有四种情况:
核心操作: 让cur的右节点充当parent的左节点, 然后让parent整体充当cur的右节点
想办法让左边的高度降下来
— — 把cur的左右高度差降低
搜索树的特性
— — 中序遍历是有序的
— — cur的右节点 (b
) 充当parent的左节点是合理的, parent充当 cur的右也是合理的cur的左右子树的高度是相等的, 都为 h+1;
void RotateL(Node* parent)
{
// 每次旋转都++
++RotateCount;
// 提前保存grandfather节点, 保证后面的链接是正确的
Node* cur = parent->_right;
Node* grandfather = parent->_parent;
Node* curleft = cur->_left;
// 旋转核心
parent->_right = curleft;
cur->_left = parent;
// 更新父亲
// 1. parent && curleft
if (curleft)
{
curleft->_parent = parent;
}
parent->_parent = cur;
// 2.更新cur
// cur要充当起parent的责任, 向上进行连接
if (grandfather == nullptr)
{
cur->_parent = nullptr;
_root = cur;
}
else
{
// 判读cur应该位于grandfather节点的哪一侧
// 1. 向下进行链接
if (grandfather->_left == parent)
{
grandfather->_left = cur;
}
else
{
grandfather->_right = cur;
}
// 2. 向上进行链接
cur->_parent = grandfather;
}
// 更新平衡因子
cur->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
++RotateCount;
Node* cur = parent->_left;
Node* grandfather = parent->_parent;
Node* curright = cur->_right;
// 旋转核心
parent->_left = curright;
cur->_right = parent;
// 更新链接关系
// 1. parent && curright
if (curright)
{
curright->_parent = parent;
}
parent->_parent = cur;
// 2.更新cur
if (grandfather == nullptr)
{
cur->_parent = nullptr;
_root = cur;
}
else
{
if (grandfather->_left == parent)
{
grandfather->_left = cur;
}
else
{
grandfather->_right = cur;
}
cur->_parent = grandfather;
}
cur->_bf = parent->_bf = 0;
}
左右双旋的本质
是:b
)充当cur的右子树,c
)充当parent的左子树,⇒ 那么 更新平衡因子, 也是要看curright的左右子树(b 和 c的高度情况)
:
cur->bf = 0;
curright->_bf = 0;
parent->_bf = 0;
cur->bf = 0;
curright->_bf = 0;
parent->_bf = 1;
cur->bf = -1;
curright->_bf = 0;
parent->_bf = 0;
左右双旋的完整代码
void RotateLR(Node* parent)
{
// 提前保存一份, 后面的左右旋转中会发生变化的
Node* cur = parent->_left;
Node* curright = cur->_right;
RotateL(parent->_left);
RotateR(parent);
// 更新平衡因子
if (curright->_bf == 0)
{
cur->_bf = 0;
parent->_bf = 0;
curright->_bf = 0;
}
else if (curright->_bf == 1)
{
cur->_bf = -1;
parent->_bf = 0;
curright->_bf = 0;
}
else if (curright->_bf == -1)
{
cur->_bf = 0;
parent->_bf = 1;
curright->_bf = 0;
}
}
b
) 充当 parent的右子树c
) 充当cur的左子树⇒ 那么 更新平衡因子, 也是要看curleft的左右子树(b 和 c的高度情况)
:
cur->bf = 0;
curleft->_bf = 0;
parent->_bf = 1;
cur->bf = 1;
curleft->_bf = 0;
parent->_bf = 0;
cur->bf = 0;
curleft->_bf = 0;
parent->_bf = -1;
右左双旋的完整代码
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
RotateR(parent->_right);
RotateL(parent);
// 更新平衡因子
if (curleft->_bf == 0)
{
cur->_bf = 0;
parent->_bf = 0;
curleft->_bf = 0;
}
else if (curleft->_bf == 1)
{
cur->_bf = 0;
parent->_bf = -1;
curleft->_bf = 0;
}
else if (curleft->_bf == -1)
{
cur->_bf = 1;
parent->_bf = 0;
curleft->_bf = 0;
}
}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = _root;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// 链接
cur = new Node(kv);
if (cur->_kv.first > parent->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
parent = cur->_parent;
// 更新平衡因子
while (parent)
{
// 1. 先更新一下parent
// 新插在右
if (parent->_right == cur)
{
parent->_bf++;
}
else // 新插在左
{
parent->_bf--;
}
// 2. 检查更新后的parent是否影响平衡
if (parent->_bf == 0)
{
// 停止更新
break;
}
else if(parent->_bf == 1 || parent->_bf == -1)
{
// 继续向上更新
cur = parent;
parent = parent->_parent;
}
// 需要旋转, 来保持平衡
else if (parent->_bf == 2 || parent->_bf == -2)
{
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else
{
assert("平衡因子更新错误!");
}
// 旋转结束, 就停止更新平衡因子
break;
}
}
return true;
}
我们算的平衡因子有可能不对
)检查程序
int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
return 0;
int left = Height(root->_left);
int right = Height(root->_right);
return left > right ? left + 1 : right + 1;
}
bool Isbalance()
{
return Isbalance(_root);
}
bool Isbalance(Node* root)
{
if (root == nullptr)
return true;
int lheight = Height(root->_left);
int rheight = Height(root->_right);
if (root->_bf != rheight - lheight || abs(rheight - lheight) > 1)
{
cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
return false;
}
// 继续检查下一个支树
return Isbalance(root->_left) && Isbalance(root->_right);
}
GetRotateCount
int GetRoateCount()
{
return RotateCount;
}
验证代码:
void avl_test()
{
const int N = 100000;
vector<int> v;
v.reserve(N);
// srand((unsigned int)time(nullptr));
for (size_t i = 0; i < N; i++)
{
// int ret = rand();
// v.push_back(ret);
v.push_back(i);
}
muyu::AVLTree<int, int> avl;
for (auto e : v)
{
avl.Insert(make_pair(e, e));
}
cout << "AVL树是否达标-> " << avl.Isbalance() << endl;
cout << "AVL树的高度-> " << avl.Height() << endl;
cout << "AVL树旋转的次数-> " << avl.GetRoateCount() << endl;
}
int main()
{
avl_test();
return 0;
}
运行结果:
AVL树是否达标-> 1
AVL树的高度-> 17
AVL树旋转的次数-> 99983
#pragma once
#include
#include
using namespace std;
namespace muyu
{
template<class K, class V>
struct AVLTreeNode
{
public:
AVLTreeNode(const pair<K,V>& kv)
:_kv(kv)
{}
public:
pair<K, V> _kv;
AVLTreeNode<K, V>* _left = nullptr;
AVLTreeNode<K, V>* _right = nullptr;
AVLTreeNode<K, V>* _parent = nullptr;
int _bf = 0;
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(nullptr)
{}
void RotateL(Node* parent)
{
// 每次旋转都++
++RotateCount;
// 提前保存grandfather节点, 保证后面的链接是正确的
Node* cur = parent->_right;
Node* grandfather = parent->_parent;
Node* curleft = cur->_left;
// 旋转核心
parent->_right = curleft;
cur->_left = parent;
// 更新父亲
// 1. parent && curleft
if (curleft)
{
curleft->_parent = parent;
}
parent->_parent = cur;
// 2.更新cur
// cur要充当起parent的责任, 向上进行连接
if (grandfather == nullptr)
{
cur->_parent = nullptr;
_root = cur;
}
else
{
// 判读cur应该位于grandfather节点的哪一侧
// 1. 向下进行链接
if (grandfather->_left == parent)
{
grandfather->_left = cur;
}
else
{
grandfather->_right = cur;
}
// 2. 向上进行链接
cur->_parent = grandfather;
}
// 更新平衡因子
cur->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
++RotateCount;
Node* cur = parent->_left;
Node* grandfather = parent->_parent;
Node* curright = cur->_right;
// 旋转核心
parent->_left = curright;
cur->_right = parent;
// 更新链接关系
// 1. parent && curright
if (curright)
{
curright->_parent = parent;
}
parent->_parent = cur;
// 2.更新cur
if (grandfather == nullptr)
{
cur->_parent = nullptr;
_root = cur;
}
else
{
if (grandfather->_left == parent)
{
grandfather->_left = cur;
}
else
{
grandfather->_right = cur;
}
cur->_parent = grandfather;
}
cur->_bf = parent->_bf = 0;
}
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
RotateR(parent->_right);
RotateL(parent);
// 更新平衡因子
if (curleft->_bf == 0)
{
cur->_bf = 0;
parent->_bf = 0;
curleft->_bf = 0;
}
else if (curleft->_bf == 1)
{
cur->_bf = 0;
parent->_bf = -1;
curleft->_bf = 0;
}
else if (curleft->_bf == -1)
{
cur->_bf = 1;
parent->_bf = 0;
curleft->_bf = 0;
}
}
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
RotateL(parent->_left);
RotateR(parent);
// 更新平衡因子
if (curright->_bf == 0)
{
cur->_bf = 0;
parent->_bf = 0;
curright->_bf = 0;
}
else if (curright->_bf == 1)
{
cur->_bf = -1;
parent->_bf = 0;
curright->_bf = 0;
}
else if (curright->_bf == -1)
{
cur->_bf = 0;
parent->_bf = 1;
curright->_bf = 0;
}
}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = _root;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// 链接
cur = new Node(kv);
if (cur->_kv.first > parent->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
parent = cur->_parent;
// 更新平衡因子
while (parent)
{
// 1. 先更新一下parent
// 新插在右
if (parent->_right == cur)
{
parent->_bf++;
}
else // 新插在左
{
parent->_bf--;
}
// 2. 检查更新后的parent是否影响平衡
if (parent->_bf == 0)
{
break;
}
else if(parent->_bf == 1 || parent->_bf == -1)
{
// 继续向上更新
cur = parent;
parent = parent->_parent;
}
// 需要旋转, 来保持平衡
else if (parent->_bf == 2 || parent->_bf == -2)
{
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else
{
assert("平衡因子更新错误!");
}
break;
}
}
return true;
}
int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
return 0;
int left = Height(root->_left);
int right = Height(root->_right);
return left > right ? left + 1 : right + 1;
}
bool Isbalance()
{
return Isbalance(_root);
}
bool Isbalance(Node* root)
{
if (root == nullptr)
return true;
int lheight = Height(root->_left);
int rheight = Height(root->_right);
if (root->_bf != rheight - lheight || abs(rheight - lheight) > 1)
{
cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
return false;
}
return Isbalance(root->_left) && Isbalance(root->_right);
}
int GetRoateCount()
{
return RotateCount;
}
private:
Node* _root = nullptr;
int RotateCount = 0;
};
}
富家不用买良田,书中自有千钟粟。
安居不用架高堂,书中自有黄金屋。
出门无车毋须恨,书中有马多如簇。
娶妻无媒毋须恨,书中有女颜如玉。
男儿欲遂平生志,勤向窗前读六经。
— — 赵恒《劝学诗》