#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
template <class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair <K, V> _kv;
int _bf; //balance factor 平衡因子
AVLTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
template <class K, class V>
struct AVLTree
{
typedef AVLTreeNode<K, V> node;
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new node(kv);
return true;
}
node* cur = _root;
node* parent = nullptr;
//先找到插入的位置
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//找到对应节点后进行链接
cur = new node(kv);
if (kv.first < parent->_kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//插入结束后,需要控制平衡,平衡是通过平衡因子来看的,所以插入完第一步先更新平衡因子
while (parent)//由于最坏情况可能会一直向上更新到跟节点,所以这里用parent不为空
{
//先更新parent
if (cur == parent->_left)
{
parent->_bf++;
}
else if (cur == parent->_right)
{
parent->_bf--;
}
//向上更新
if (parent->_bf == 0)
{
break;
}
else if (abs(parent->_bf) == 1)
{
parent = parent->_parent;
cur = cur->_parent;
}
else if (abs(parent->_bf) == 2)
{
//说明parent所在子树已经不平衡,需要旋转处理
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(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;
}
private:
node* _root = nullptr;
void RotateL(node* parent)
{
//这里需要分类讨论一共有两种情况
//1.parent是根节点,需要更改根节点
//2.parent是子树,需要更改原parent的上一层指向
node* subr = parent->_right;
node* subrl = subr->_left;
node* ppnode = parent->_parent;
subr->_left = parent;
parent->_right = subrl;
parent->_parent = subr;
if (subrl)//subrl有可能为空
{
subrl->_parent = parent;
}
//1.根节点情况
if (_root == parent)
{
_root = subr;
subr->_parent = nullptr;
}
//2.parent是子树,需要更改parent的上一层节点的孩子指向
else
{
//判断ppnode指向左还是右
if (ppnode->_left == parent)
{
ppnode->_left = subr;
}
else
{
ppnode->_right = subr;
}
subr->_parent = ppnode;
}
subr->_bf = parent->_bf = 0;
}
void RotateR(node* parent)
{
//这里同左旋转
node* subl = parent->_left;
node* sublr = subl->_right;
node* ppnode = parent->_parent;
subl->_right = parent;
parent->_parent = subl;
parent->_left = sublr;
if (sublr)
{
sublr->_parent = parent;
}
if (_root == parent)
{
_root = subl;
subl->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subl;
}
else
{
ppnode->_right = subl;
}
subl->_parent = ppnode;
}
subl->_bf = parent->_bf = 0;
}
void RotateLR(node* parent)
{
node* subl = parent->_left;
node* sublr = subl->_right;
int bf = sublr->_bf;
RotateL(parent->_left);
RotateR(parent);
sublr->_bf = 0;
if (bf == 1)
{
parent->_bf = 0;
subl->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
subl->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subl->_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);
subrl->_bf = 0;
if (bf == 1)
{
subr->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subr->_bf = 1;
parent->_bf = 0;
}
else if(bf == 0)
{
parent->_bf = 0;
subr->_bf = 0;
}
}
};
红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的。
红黑树的性质
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
规则3决定了红色节点的数量,规则4决定了黑色节点的数量
极限最长:一黑一红
极限最短:全黑
先按照二叉搜索树的规则插入节点,并将节点的颜色默认设为红,再看有没有违反红黑树规则,如果违反则看下面的红黑树修改规则。
红黑树修改规则:
1.空节点
2.父节点是黑,则直接插入红
3.父节点是红
(1)叔叔节点(父节点旁边的节点)存在且为红,则变色继续向上处理
(2)叔叔节点不存在且为黑,旋转+变色,这里对应四种旋转看情况选择(左单旋,右单旋,左右双旋,右左双旋)
父节点为红的情况1体现出红黑树与avl树的不一样,这里先考虑变色,如果叔叔节点存在且为红,就说明最长路径还没到最短路径的两倍,所以先不考虑旋转。而avl树的判定则更加严格,只要差距超过1就要开始旋转。