平衡二叉树
struct AVLtreenode
{
int data; //可以换成键值对类型,比较键值
int bf; //平衡因子的值是左子树高度减去右子树的高度,因此+1代表左子树高1,0代表左右一样高,-1代表右子树高1.
AVLtreenode *lchild, *rchild;
AVLtreenode() : data(0), bf(0), lchild(NULL), rchild(NULL) {}
AVLtreenode(int val) : data(val), bf(0), lchild(NULL), rchild(NULL) {}
};
bool insert(node *now, int value, bool &taller)//taller为长高因子,true为长高即高度加1,false为未长高
第一步:树空,插入新节点
//树空,插入新节点
if (!now)
{
now = new node(value);
taller = true;
return true;
}
第二步:树非空,根据大小关系进入左右子树,直到树空
//存在相同值,插入失败,可以把这里改成key值,key值不能相同
//taller为长高因子,如果子树长高,那么递归返回的taller为true
if (value == now->data)
{
taller = false;
return false;
}
else if (value < now->data) //进入左子树
{
if (!insert(now->lchild, value, taller)) //进入左子树,如果插入失败返回false
return false;
//根据高度调整
//adjust();
}
else //进入右子树
{
if (!insert(now->rchild, value, taller)) //进入右子树,如果插入失败返回false
return false;
//根据高度调整
//adjust();
}
return true;
第三步:递归逐层返回长高因子,根据长高因子进行旋转
分四种情况:
- 1、在左子树的左子树插入新节点(右旋)
- 2、在左子树的右子树插入新节点(先左旋再右旋)
- 3、在右子树的左子树插入新节点(先右旋再左旋)
- 4、在右子树的右子树插入新节点(左旋)
左旋:
void L_rotate(node *&now) //左旋
{
node *right = now->rchild;
now->rchild = right->lchild;
right->lchild = now;
now = right;
}
右旋:
void R_rotate(node *&now) //右旋
{
node *left = now->lchild;
now->lchild = left->rchild;
left->rchild = now;
now = left;
}
先对前两种情况进行分析
- 1、在左子树的左子树插入新节点(右旋)
- 2、在左子树的右子树插入新节点(先左旋再右旋)
左子树长高(还未分左子树的左右子树情况,这些情况在leftbalance中分析)
else if (value < now->data) //进入左子树
{
if (!insert(now->lchild, value, taller)) //进入左子树,如果插入失败返回false
return false;
//左子树长高(还未分左子树的左右子树情况,这些情况在leftbalance中分析)
if (taller)
{
switch (now->bf)
{
//原本左子树高1,现因左子树增高,需进行左平衡处理
case 1:
leftbalance(now);
taller = false;
break;
//原本左右子树平衡,现左子树增高
case 0:
now->bf = 1;
taller = true;
break;
//原本右子树高1,现左子树增高,左右子树平衡
case -1:
now->bf = 0;
taller = false;
break;
}
}
}
左子树再分左子树的左右子树,也就是右旋和左右旋:
void leftbalance(node *&now)
{
node *lc = now->lchild;
if (lc->bf == 1) //新节点插在now的左孩子的左子树上
{
//修改now及左右孩子的平衡因子
now->bf = lc->bf = 0;
//右旋
R_rotate(now);
}
else //新节点插在now的左孩子的右子树上
{
//修改now及左右孩子的平衡因子
node *rd = lc->rchild;
if (rd->bf == 1)
{
now->bf = -1;
lc->bf = 0;
}
else if (rd->bf == 0)
{
now->bf = lc->bf = 0;
}
else
{
now->bf = 0;
lc->bf = 1;
}
rd->bf = 0;
//左右旋
L_rotate(now->lchild);
R_rotate(now);
}
}
然后对后两种情况进行分析
- 3、在右子树的左子树插入新节点(先右旋再左旋)
- 4、在右子树的右子树插入新节点(左旋)
右子树长高(还未分右子树的左右子树情况,这些情况在rightbalance中分析)
else //进入右子树
{
if (!insert(now->rchild, value, taller)) //进入右子树,如果插入失败返回false
return false;
//右子树长高(还未分右子树的左右子树情况,这些情况在rightbalance中分析)
if (taller)
{
switch (now->bf)
{
//原本左子树高,现因右子树增高,左右子树平衡
case 1:
now->bf = 0;
taller = false;
break;
//原本左右子树平衡,现右子树增高
case 0:
now->bf = -1;
taller = true;
break;
//原本右子树高1,现右子树增高,需进行右平衡处理
case -1:
rightbalance(now);
taller = false;
break;
}
}
}
右子树再分右子树的左右子树,也就是左旋右左旋
void rightbalance(node *&now)
{
node *rc = now->rchild;
if (rc->bf == -1) //新节点插在now的右孩子的右子树上
{
now->bf = rc->bf = 0;
L_rotate(now);
}
else //新节点插在now的右孩子的左子树上
{
//修改now及左右孩子的平衡因子
node *ld = rc->lchild;
if (ld->bf == 1)
{
now->bf = 0;
rc = 0;
}
else if (ld->bf == 0)
{
now->bf = rc->bf = 0;
}
else
{
now->bf = 1;
rc->bf = 0;
}
ld->bf = 0;
//右左旋
R_rotate(now->rchild);
L_rotate(now);
}
}
这就是平衡二叉树的插入实现,对于搜索等操作,比较简答在这里就不费周折,整体代码请看
https://blog.csdn.net/weixin_48673014/article/details/119007267?spm=1001.2014.3001.5501
红黑树
红黑树结构
typedef struct node
{
int val;
struct node *par;
struct node *left;
struct node *right;
int color;
}
红黑树的特性
- 每个节点或者是黑色,或者是红色。
- 根节点是黑色。
- 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
- 如果一个节点是红色的,则它的子节点必须是黑色的。
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
Q:红黑树为何查找为log(n)?
A:形象的证明:我们假设一颗全是黑节点二叉平衡树,然后我们向其中插入红节点,根据红黑树特性,我们发现,最长的一条根节点路径到叶子节点是红黑相间的,这条路径节点数也就是黑节点数的二倍,原二叉平衡树访问log(n),那么加入红节点,顶多2log(n)。
因为红黑树有更宽松的限制,因此红黑树减少了许多不必要的旋转,因此平均效率要比平衡二叉树高。
红黑树的插入
红黑树的插入和平衡二叉树差不多:
首先满足三个原则:
1、插入的节点都是红节点
2、黑节点下直接插入
3、根节点一定是黑节点
因此只有当在红节点后插入时才需要改变
- 父亲和叔叔都是红点,层次变换,父亲和叔叔变成黑节点,父亲的父亲变成红节点,以此类推
- 叔叔是黑节点或NULL完全符合平衡二叉树旋转规律
即
- 1、红节点在祖宗节点的左子树且在红节点的左子树插入新节点(右旋)
- 2、红节点在祖宗节点的左子树且在红节点的右子树插入新节点(先左旋再右旋)
- 3、红节点在祖宗节点的右子树且在红节点的左子树插入新节点(先右旋再左旋)
- 4、红节点在祖宗节点的右子树且在红节点的右子树插入新节点(右旋)
红黑树插入代码:
参考:https://blog.csdn.net/tanrui519521/article/details/80980135
bool Insert(const K& key, const V& value)
{
//根节点为空
if (_root == NULL)
{
_root = new Node(key,value);
_root->_color = BLACK;
return true;
}
//根节点不为空
//找到新节点插入位置
Node* parent = NULL;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key >key)
{
parent = cur;
cur = cur->_left;
}
else
return false;
}
//插入新节点
cur = new Node(key, value);
cur->_color = RED;
if (parent->_key > key)
{
parent->_left = cur;
cur->_parent = parent;
}
else//parent->_key < key
{
parent->_right = cur;
cur->_parent = parent;
}
//插入节点后颜色的调整
while (parent && parent->_color == RED)
{
Node* grandfather = parent->_parent;//grandfather颜色一定为黑色
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//uncle存在且为红
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else//uncle不存在/uncle存在且为黑
{
if (cur == parent->_right)
{
RotateL(parent);
swap(parent, cur);
}
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
}
else//grandfather->_right==parent
{
Node* uncle = grandfather->_left;
//uncle存在且为红
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else//不存在/存在且为黑
{
if (cur == parent->_left)
{
RotateR(parent);
swap(cur, parent);
}
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
}
}//end while (parent && parent->_color == RED)
_root->_color = BLACK;
return true;
}