前言
对于之前普通的二叉搜索树,其搜索的效率依靠树的形状来决定,如下:
可以看到 A图 中的树比较彭亨,搜索一个元素的效率接近 O(logN) ;而 B图 中的形状也符合搜索二叉树,但是很不平衡,这时的搜索效率就变成 O(N) 了(如搜索值为4的节点)
可以看到,如果搜索二叉树不平衡,他的搜索的效率不确定,在 O(logN) 和 O(N) 之间,可能有暴击;
那么对此,既然不平衡,那我们就给出平衡的两种方法:
1.构成AVL树
2.构成红黑树
这两种方法都是为了平衡搜索二叉树而生
AVL树
1. AVL树的概念和性质
它的出现是为了 搜索二叉树的平衡,那就来了解AVL树的概念和性质,也就是什么是AVL,并且作为一颗AVL树的要求是什么
首先AVL树是一颗树,它要求任何一个节点的左右子树的高度差不超过一,以此来保证平衡,是AVL树的核心
来看看它的做法
与搜索二叉树不同,它将二叉链,变成了三叉链(多了指向父节点的指针),并且引入了平衡因子这个东西;这些都是为了方便并且为了树的一个平衡引入
三叉链和平衡因子和下面AVL树属性的图结合起来看更直观
三叉链也就是多了一个 parent指针 指向该节点的父节点,这一操作可以让节点找到其祖先
平衡因子是指该节点的 右子树高度 减去 左子树高度的值,而根据AVL树的性质(要求任何一个节点的左右子树的高度差不超过一),也就是平衡因子 _bf 的值域为 { -1, 0, 1 } ,如果平衡因子 _bf 的值出现为 2 或者 -2 ,说明该树已经不平衡,此时需要进行停止调整,先进行旋转操作
2. AVL树类的属性
这里的节点值类型简化为了int类型,原为 pair
3. AVL树的插入函数
而AVL树的重点呢,也就是AVL树是如何让树达到平衡的呢?就是每插入了一个节点后,进行了一些调整,如果插入节点后不平衡,那么会将其调整成平衡
插入后对插入节点祖先的平衡因子进行调整,当调整过程中,有平衡因子出现异常时,此时通过旋转的方式,让树继续保持平衡
当平衡因子出现异常即是 平衡因子的值更新为 2 或者 -2 时,说明树现在已经不平衡,需要进行旋转
分情况讨论:
现在平衡因子已经出现了不平衡,为什么旋转之后,树就可以继续平衡呢?
具体看到旋转 (这里以左单旋为例):
定义节点指针 parent(图中的节点值为30), cur(图中的60)
左单旋是将 cur 的左孩子给给 parent 的右, 再把 parent 赋值给 cur 的左,旋转完成
再进行平衡因子的更新:parent 和 cur 的平衡因子 _bf 都变成 0
左单旋具体代码如下:
void RotateL(Node* parent)
{
assert(parent);
Node* cur = parent->_right;
Node* cur_left = cur->_left;
// 改变节点之间的关系
parent->_right = cur_left;
cur->_left = parent;
// 改变_parent的指向
//原本 parent 的 _parent
Node* ppNode = parent->_parent;
//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
if (ppNode)
{
if (ppNode->_left == parent)
{
ppNode->_left = cur;
}
else
{
ppNode->_right = cur;
}
cur->_parent = ppNode;
}
//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
else
{
_root = cur;
cur->_parent = nullptr;
}
if (cur_left)
{
cur_left->_parent = parent;
}
parent->_parent = cur;
// 更新平衡因子
cur->_bf = parent->_bf = 0;
}
对左单旋的总结是:当不平衡的树进行左单旋之后,树又被调整为平衡,然后再进行平衡因子的更新
那么右单旋的思路是一样的,自己可以进行推理
而要进行双旋时候,可以再进行讨论(这里以右左旋进行讨论):
右左旋:定义 parent(下图节点值为30), cur(下图节点值为90), cur_left(下图节点值为60)
双旋都是两大步,这里右左旋就是先进行右旋,再进行左旋,旋转结束;对,这里对刚刚实现的单旋代码进行了复用,复用yyds
具体是对 cur 先进行右旋,再对 parent 进行左旋,如下
最后进行平衡因子的调整,但是这里特别容易忘记第三种情况的讨论,AVL树的这个细节得注意:
右左旋的代码如下:
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* cur_left = cur->_left;
int bf = cur_left->_bf;
RotateR(parent->_right);
RotateL(parent);
parent->_bf = 0;
cur->_bf = 0;
cur_left->_bf = 0;
if (bf == -1)
{
cur->_bf = 1;
}
if (bf == 1)
{
parent->_bf = -1;
}
}
那么左右旋的思路是一样的,自己可以进行推理
4. 总结
而进行何种旋转本质是取决于 parent cur 和 cur_left (或cur_right)之间的关系,若他们三个所连成的是直线,那么进行单旋操作,如果为折线,那就进行双旋操作;而这里的平衡因子刚好可以体现他们之间的关系,所以上图中根据平衡因子来决定单双旋和左右旋转
AVL树构造和插入函数,测试函数:
这里的节点值类型简化为了int类型,原为 pair
#pragma once
#include <iostream>
using namespace std;
#include <string>
#include <array>
#include <deque>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>
#include <assert.h>
#include <windows.h>
#include <ctime>
namespace zhuandrong
{
class AVLTreeNode
{
typedef AVLTreeNode Node;
public:
int _val;
Node* _left;
Node* _right;
Node* _parent;
int _bf;
public:
AVLTreeNode(const int val = 0)
: _val(val)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{}
};
class AVLTree
{
typedef AVLTreeNode Node;
protected:
void RotateL(Node* parent)
{
assert(parent);
Node* cur = parent->_right;
Node* cur_left = cur->_left;
// 改变节点之间的关系
parent->_right = cur_left;
cur->_left = parent;
// 改变_parent的指向
//原本 parent 的 _parent
Node* ppNode = parent->_parent;
//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
if (ppNode)
{
if (ppNode->_left == parent)
{
ppNode->_left = cur;
}
else
{
ppNode->_right = cur;
}
cur->_parent = ppNode;
}
//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
else
{
_root = cur;
cur->_parent = nullptr;
}
if (cur_left)
{
cur_left->_parent = parent;
}
parent->_parent = cur;
// 更新平衡因子
cur->_bf = parent->_bf = 0;
}
//void RotateL(Node* parent)
//{
// Node* cur = parent->_right;
// Node* cur_left = cur->_left;
// parent->_right = cur_left;
// cur->_left = parent;
// Node* ppNode = parent->_parent;
// if (cur_left)//右孩子可能存在,也可能不存在,所以需要判断,需要在parent改变前判断
// {
// cur_left->_parent = parent;
// }
// parent->_parent = cur;
// if (parent == _root)//parent可能是根节点,也可能不是根节点
// {
// _root = cur;
// cur->_parent = nullptr;
// }
// else
// {
// if (ppNode->_left == parent)
// {
// ppNode->_left = cur;
// }
// else
// {
// ppNode->_right = cur;
// }
// cur->_parent = ppNode;
// }
// cur->_bf = parent->_bf = 0;//将平衡因子调整
//}
void RotateR(Node* parent)
{
assert(parent);
Node* cur = parent->_left;
Node* cur_right = cur->_right;
// 改变节点之间的关系
parent->_left = cur_right;
cur->_right = parent;
// 改变_parent的指向
//原本 parent 的 _parent
Node* ppNode = parent->_parent;
//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
if (ppNode)
{
if (ppNode->_left == parent)
{
ppNode->_left = cur;
}
else
{
ppNode->_right = cur;
}
cur->_parent = ppNode;
}
//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
else
{
_root = cur;
cur->_parent = nullptr;
}
if (cur_right)
{
cur_right->_parent = parent;
}
parent->_parent = cur;
// 更新平衡因子
cur->_bf = parent->_bf = 0;
}
//void RotateR(Node* parent)
//{
// Node* cur = parent->_left;
// Node* curRight = cur->_right;
// parent->_left = curRight;
// cur->_right = parent;
// Node* ppNode = parent->_parent;
// if (curRight)//右孩子可能存在,也可能不存在,所以需要判断,需要在parent改变前判断
// {
// curRight->_parent = parent;
// }
// parent->_parent = cur;
// if (parent == _root)//parent可能是根节点,也可能不是根节点
// {
// _root = cur;
// cur->_parent = nullptr;
// }
// else
// {
// if (ppNode->_left == parent)
// {
// ppNode->_left = cur;
// }
// else
// {
// ppNode->_right = cur;
// }
// cur->_parent = ppNode;
// }
// cur->_bf = parent->_bf = 0;//将平衡因子调整
//}
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* cur_left = cur->_left;
int bf = cur_left->_bf;
RotateR(parent->_right);
RotateL(parent);
/*parent->_bf = cur_left->_bf = 0;
cur->_bf = flag;*/
parent->_bf = 0;
cur->_bf = 0;
cur_left->_bf = 0;
if (bf == -1)
{
cur->_bf = 1;
}
if (bf == 1)
{
parent->_bf = -1;
}
}
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* cur_right = cur->_right;
int bf = cur_right->_bf;
RotateL(parent->_left);
RotateR(parent);
/*parent->_bf = parent->_parent->_bf = 0;
parent->_parent->_left->_bf = -1;*/
cur->_bf = 0;
parent->_bf = 0;
cur_right->_bf = 0;
if (bf == 1)
{
cur->_bf = -1;
}
if (bf == -1)
{
parent->_bf = 1;
}
}
protected:
Node* _root;
public:
AVLTree()
: _root(nullptr)
{}
AVLTree(vector<int> v)
{
for (auto& e : v)
{
if (e == 8)
{
int i = 0;
}
insert(e);
assert(IsBalance());
}
}
bool insert(int val)
{
// 判断是否有根节点
if (_root)
{
//先找到合适的插入位置
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
cout << "插入的元素值已重复" << endl;
return false;
}
}
cur = new Node(val);
cur->_parent = parent;
if (val < parent->_val)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
//对平衡因子进行更新
while (parent)
{
if (cur == parent->_left)
{
--parent->_bf;
}
else if (cur == parent->_right)
{
++parent->_bf;
}
//若 parent->_bf == 0,表明平衡因子调整结束
if (!parent->_bf)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
//若parent->_bf == 2 && cur->_bf == 1,将parent进行左单旋
else if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
break;
}
//若parent->_bf == 2 && cur->_bf == -1,将parent进行右左单旋
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
break;
}
//若parent->_bf == -2 && cur->_bf == -1,将parent进行右单旋
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
break;
}
//若parent->_bf == -2 && cur->_bf == 1,将parent进行左右单旋
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
break;
}
else
{
assert(false);
}
}
}
else
{
_root = new Node(val);
}
return true;
}
int TreeHight(Node* root)
{
if (root == nullptr)
return 0;
int leftHight = TreeHight(root->_left);
int rightHight = TreeHight(root->_right);
return leftHight > rightHight ? leftHight + 1 : rightHight + 1;
}
void Inorder()
{
_Inorder(_root);
}
bool IsBalance()
{
return _IsBalance(_root);
}
private:
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_val << endl;
_Inorder(root->_right);
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int leftHight = TreeHight(root->_left);
int rightHight = TreeHight(root->_right);
//检查平衡因子对不对
if (rightHight - leftHight != root->_bf)
{
cout << "平衡因子出现异常" << endl;
cout << rightHight - leftHight << endl;
return false;
}
//需要递归检查是否平衡
return (leftHight - rightHight <= 1 && leftHight - rightHight >= -1)
&& _IsBalance(root->_left) && _IsBalance(root->_right);
}
};
void test_AVLTree()
{
/*vector<int> v = { 6, 4, 32, 2, 7, 8 };
AVLTree avl_tree(v);
assert(avl_tree.IsBalance());*/
AVLTree avl_tree;
srand((unsigned int)time(NULL));
for (int i = 1000; i >= 0; --i)
{
avl_tree.insert(rand());
//avl_tree.insert(i);
assert(avl_tree.IsBalance());
}
//srand((unsigned int)time(0));
//const size_t N = 10000;
//for (size_t i = 0; i < N; ++i)
//{
// size_t x = rand();
// avl_tree.insert(x);
// //cout << t.IsBalance() << endl;
//}
//avl_tree.Inorder();
//cout << avl_tree.IsBalance() << endl;
}
}
红黑树
1. 红黑树的概念和性质(什么是红黑树,并且作为一颗红黑树的要求)
以上是红黑树的概念和性质,在红黑树里不仅仅要考虑 cur 、curleft 、 parent 还要引入 grandfather,具体为什么看到后面就知道了
2. 红黑树类的属性
是使用枚举枚举出 红 黑 两种情况
3. 红黑树的插入函数
还是分情况讨论:
4. 总结
根据性质可以分为以下情况:
可以看出第一种情况(uncle存在且为黑)只需要变色即可,在看是否继续向上调整
第二种情况就要旋转了,而如何旋转本质图中也详细说了,取决于 grandfather、parent、cur 之间的关系,其实AVL树的旋转的决定因素也在此,只是转换成平衡因子进行描述
关于红黑,具体的思想关键体现在插入函数中,复习请自行分析出两种大情况,和情况二的细分,然后写出插入函数,才算过关
总体代码:
#pragma once
#include <iostream>
using namespace std;
#include <string>
#include <array>
#include <deque>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>
#include <assert.h>
#include <windows.h>
#include <ctime>
namespace zhuandrong
{
enum Color
{
RED,
BLACK
};
template<typename K, typename T>
class RBTreeNode
{
typedef RBTreeNode Node;
public:
pair<K, T> _t;
Node* _left;
Node* _right;
Node* _parent;
Color color;
public:
RBTreeNode(pair<K, T> t)
: _t(t)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, color(RED)
{}
};
template<typename K, typename T>
class RBTree
{
typedef RBTreeNode<K, T> Node;
protected:
Node* _root;
protected:
void RotateL(Node* parent)
{
assert(parent);
Node* cur = parent->_right;
Node* cur_left = cur->_left;
// 改变节点之间的关系
parent->_right = cur_left;
cur->_left = parent;
// 改变_parent的指向
//原本 parent 的 _parent
Node* ppNode = parent->_parent;
//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
if (ppNode)
{
if (ppNode->_left == parent)
{
ppNode->_left = cur;
}
else
{
ppNode->_right = cur;
}
cur->_parent = ppNode;
}
//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
else
{
_root = cur;
cur->_parent = nullptr;
}
if (cur_left)
{
cur_left->_parent = parent;
}
parent->_parent = cur;
}
void RotateR(Node* parent)
{
assert(parent);
Node* cur = parent->_left;
Node* cur_right = cur->_right;
// 改变节点之间的关系
parent->_left = cur_right;
cur->_right = parent;
// 改变_parent的指向
//原本 parent 的 _parent
Node* ppNode = parent->_parent;
//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 cur
if (ppNode)
{
if (ppNode->_left == parent)
{
ppNode->_left = cur;
}
else
{
ppNode->_right = cur;
}
cur->_parent = ppNode;
}
//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根
else
{
_root = cur;
cur->_parent = nullptr;
}
if (cur_right)
{
cur_right->_parent = parent;
}
parent->_parent = cur;
}
void RotateRL(Node* parent)
{
RotateR(parent->_right);
RotateL(parent);
}
void RotateLR(Node* parent)
{
RotateL(parent->_left);
RotateR(parent);
}
public:
RBTree()
: _root(nullptr)
{}
RBTree(vector <pair<K, T>> v)
{
for (auto& e : v)
{
if (e.second == 5)
{
int i = 0;
}
insert(e);
}
}
bool insert(const pair<K, T> t)
{
// 先判断根节点是否为空
if (_root)
{
// 若不为空,先找到合适的插入位置
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (t.second < cur->_t.second)
{
parent = cur;
cur = cur->_left;
}
else if (t.second > cur->_t.second)
{
parent = cur;
cur = cur->_right;
}
else
{
//cout << "该元素已经插入,请重试" << endl;
return false;
}
}
//找到该位置后进行插入并且链接关系
cur = new Node(t);
cur->_parent = parent;
if (t.second < parent->_t.second)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
// 进行调整
while (parent && parent->color == RED)
{
Node* grandfather = parent->_parent;
Node* uncle = nullptr;
if (parent == grandfather->_left)
{
uncle = grandfather->_right;
}
else
{
uncle = grandfather->_left;
}
// 第一种情况:如果uncle存在且为红
if (uncle && uncle->color == RED)
{
//将p和u变黑,g变红
parent->color = uncle->color = BLACK;
grandfather->color = RED;
//更新,并且判断更新后的parent是否为根,不为根就继续调整;为根表明调整结束
cur = grandfather;
parent = cur->_parent;
if (parent == _root)
{
break;
}
}
// 第二种情况:如果uncle存在且为黑色或者uncle不存在
else
{
// 当 cur 为 parent 的左孩子时
if (parent->_left == cur)
{
if (grandfather->_left == parent)
{
// g
// p u
// c
//
Node* uncle = grandfather->_right;
RotateR(grandfather);
grandfather->color = cur->color = RED;
parent->color = BLACK;
}
else
{
// g g c
// u p --> u c --> g p
// c p u
//
Node* uncle = grandfather->_left;
RotateRL(grandfather);
grandfather->color = parent->color = RED;
cur->color = BLACK;
}
}
// 当 cur 为 parent 的右孩子时
else
{
if (grandfather->_right == parent)
{
// g
// u p
// c
//
Node* uncle = grandfather->_right;
RotateL(grandfather);
grandfather->color = cur->color = RED;
parent->color = BLACK;
}
else
{
// g g c
// p u --> c u --> p g
// c p u
//
Node* uncle = grandfather->_left;
RotateLR(grandfather);
grandfather->color = parent->color = RED;
cur->color = BLACK;
}
}
break;
}
}
}
// 若为空,则该元素作为根节点
else
{
_root = new Node(t);
}
_root->color = BLACK;
return true;
}
bool judge_color(Node* root, int blacksum, int blacknum)
{
if (!root)
{
if (blacknum != blacksum)
{
return false;
}
return true;
}
if (root->color == BLACK)
{
++blacknum;
}
if (root->color == RED && root->_parent && root->_parent->color == RED)
{
return false;
}
return judge_color(root->_left, blacksum, blacknum)
&& judge_color(root->_right, blacksum, blacknum);
}
bool isBalance()
{
if (!_root)
{
cout << "根为空" << endl;
return false;
}
if (_root->color == RED)
{
cout << "根的颜色为红色,非法" << endl;
return false;
}
Node* cur = _root;
int blacksum = 0;
while (cur)
{
if (cur->color == BLACK)
{
++blacksum;
}
cur = cur->_right;
}
return judge_color(_root, blacksum, 0);
}
};
void test_RBTree()
{
/*vector<pair<int, int>> v;
v.push_back(make_pair(1, 13));
v.push_back(make_pair(2, 8));
v.push_back(make_pair(3, 17));
v.push_back(make_pair(4, 1));
v.push_back(make_pair(5, 11));
v.push_back(make_pair(6, 15));
v.push_back(make_pair(7, 25));
v.push_back(make_pair(8, 6));
v.push_back(make_pair(9, 22));
v.push_back(make_pair(10, 27));
v.push_back(make_pair(11, 5));
RBTree<int, int> rb_tree(v);
cout << rb_tree.isBalance() << endl;*/
RBTree<int, int> rb_tree;
srand((unsigned int)time(NULL));
for (int i = 0; i < 1000; ++i)
{
rb_tree.insert(make_pair(i, rand()));
}
if (rb_tree.isBalance())
{
cout << "本次测试成功" << endl;
}
else
{
cout << "本次测试失败" << endl;
}
}
}