目录
红黑树和 AVL 树都是计算机科学中常用的自平衡二叉搜索树,它们通过特定的平衡规则维持树的结构,确保插入、删除、查找等操作的时间复杂度稳定在O(log n)(n 为节点数量)
1.AVL树的特性
AVL 树是最早发明的自平衡二叉搜索树,其核心特性围绕 “节点高度平衡” 展开
-
二叉搜索树的基础特性
- 对于任意节点,其左子树中所有节点的值均小于该节点的值;右子树中所有节点的值均大于该节点的值。
-
平衡条件:左右子树高度差不超过 1
- 每个节点的平衡因子(左子树高度 - 右子树高度)的绝对值必须≤1。
- 节点的 “高度” 指从该节点到最远叶子节点的路径长度(叶子节点高度为 0,空节点高度为 - 1)。
-
失衡后的调整:旋转操作
- 当插入或删除节点导致平衡因子绝对值>1 时,通过旋转(左旋、右旋、左右旋、右左旋)恢复平衡。
- 例如:左左失衡(左子树的左子树过高)→ 右旋;右右失衡→左旋;左右失衡→先左旋后右旋;右左失衡→先右旋后左旋。
- 当插入或删除节点导致平衡因子绝对值>1 时,通过旋转(左旋、右旋、左右旋、右左旋)恢复平衡。
-
优点与缺点
- 优点:平衡度高,查找效率稳定(因为树的高度严格控制在 O (log n))。
- 缺点:插入和删除操作频繁时,旋转次数多(维护成本高),适合查询多、修改少的场景。
AVL树的模拟实现
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
pair<K, V>_kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;//balace factor
//拷贝构造
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool Insert(const pair<K, 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->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
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)
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(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 RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* pParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
{
pParent->_left = subL;
subL->_parent = pParent;
}
else
{
pParent->_right = subL;
subL->_parent = pParent;
}
}
subL->_bf = 0;
parent->_bf = 0;
}
//左旋转
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* pParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (pParent==nullptr )
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == pParent->_left)
{
pParent->_left = subR;
subR->_parent = pParent;
}
else
{
pParent->_right = subR;
subR->_parent = pParent;
}
}
subR->_bf = 0;
parent->_bf = 0;
}
//左右旋转
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == 0)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_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);
if (bf == -1)
{
subRL->_bf = 0;
subR->_bf = 1;
parent->_bf = 0;
}
else if(bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == 0)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
//AVL的查找
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
//析构AVL树
~AVLTree()
{
_DestoryAVLTree(_root);
}
void InOrder()
{
_Inorder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
int Size()
{
return _Size(_root);
}
private:
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_kv.first << ':' << root->_kv.second<<endl;
_Inorder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftHeight = _Height(root->_left);
int rightHright = _Height(root->_right);
return leftHeight > rightHright ? leftHeight + 1 : rightHright + 1;
}
int _Size(Node* root)
{
if (root == nullptr)
{
return 0;
}
return _Size(root->_left)+_Size(root->_right) + 1;
}
//判断树是不是AVL树
bool _IsBalanceTree(Node* root)
{
// 空树也是AVL树
if (nullptr == root)
return true;
// 计算pRoot结点的平衡因子:即pRoot左右子树的高度差
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
// pRoot平衡因子的绝对值超过1,则一定不是AVL树
if (abs(diff) >= 2)
{
cout << root->_kv.first << "高度差异常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
// pRoot的左和右如果都是AVL树,则该树一定是AVL树
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
void _DestoryAVLTree(Node* root)
{
if (root == nullptr)
{
return;
}
_DestoryAVLTree(root->_left);
_DestoryAVLTree(root->_right);
delete root;
}
private:
Node* _root = nullptr;
};
2.红黑树的特性
红黑树通过 “颜色标记” 和一系列规则维持平衡,平衡条件比 AVL 树宽松,核心是确保 “最长路径不超过最短路径的 2 倍”
-
二叉搜索树的基础特性
- 与 AVL 树一致,左子树值<节点值<右子树值。
-
颜色规则
- 每个节点要么是红色,要么是黑色。
- 根节点必须是黑色。
- 叶子节点(NIL 节点,空节点)必须是黑色(实际实现中可能省略,仅逻辑上存在)。
- 如果一个节点是红色,其两个子节点必须是黑色(即:不存在连续的红色节点)。
- 从任意节点到其所有叶子节点的路径中,黑色节点的数量相同(称为 “黑高” 相等)。
-
失衡后的调整:变色与旋转
- 插入或删除节点后,若违反颜色规则,通过变色(改变节点颜色)和旋转(同 AVL 树的四种旋转)恢复平衡。
- 插入时新节点默认为红色(减少对黑高的影响),若父节点为红色(违反 “无连续红节点” 规则),则触发调整(如:叔叔节点为红则变色,叔叔节点为黑则旋转 + 变色)。
-
优点与缺点
- 优点:插入和删除时旋转次数少(平均 1-2 次),维护成本低,适合修改频繁的场景(如集合、映射等数据结构)。
- 缺点:平衡度不如 AVL 树严格,极端情况下查找效率略低于 AVL 树,但仍为 O (log n)。
红黑树的模拟实现
#pragma once
enum Colour
{
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
// 这里更新控制平衡也要加入parent指针
pair<K, V> _kv;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Colour _col;
RBTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
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);
cur->_col = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 链接父亲
cur->_parent = parent;
// 父亲是红色,出现连续的红色节点,需要处理
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
// g
// p u
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// g
// p u
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
// g
// u p
Node* uncle = grandfather->_left;
// 叔叔存在且为红,-》变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者存在且为黑
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* pParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
{
pParent->_left = subL;
}
else
{
pParent->_right = subL;
}
subL->_parent = pParent;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
bool isbalance()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
return false;
// 参考值
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
private:
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
// 前序遍历走到空时,意味着一条路径走完了
//cout << blackNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色结点的数量不相等的路径" << endl;
return false;
}
return true;
}
// 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续的红色结点" << endl;
return false;
}
if (root->_col == BLACK)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
int _Size(Node* root)
{
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
private:
Node* _root = nullptr;
};
特性 | AVL树 | 红黑树 |
树高 | 严格 O (log n)(更矮) | O (log n)(可能稍高) |
旋转次数 | 插入最多 2 次,删除可能更多 | 插入最多 2 次,删除最多 3 次 |
平衡依据 | 节点平衡因子(高度差≤1) | 颜色规则(最长路径≤最短路径 ×2) |
空间开销 | 需存储每个节点的高度信息 | 需存储颜色标记 |
使用场景 | 查询操作频繁 | 插入 / 删除频繁 |
总的来说:
1.AVL 树是 “严格平衡” 的二叉树,适合查询密集型场景,代价是修改操作效率低。
2.红黑树是 “近似平衡” 的二叉树,修改操作效率更高,在实际工程中应用更广泛(如 C++ STL 的 map/set、Linux 内核、Java 的 TreeMap 等)。
3.两者本质都是通过平衡规则避免二叉搜索树退化为链表(O (n) 复杂度),但平衡策略的宽松程度决定了它们的适用场景差异。