转载:https://blog.csdn.net/weixin_36194037/article/details/79440464
转载:https://www.cnblogs.com/suimeng/p/4560056.html
在学习二叉排序树的查找时,通过分析查找算法的效率可知,不同结构的二叉排序树查找效率有很大的不同,单支树(图1)的查找效率相当于顺序查找,而越趋于平衡的二叉排序树(图2)查找效率越高。因此,在二叉排序树的基础上引进了平衡二叉树。
平衡二叉树的目的是:
平衡二叉树或者是棵空树,或者是具体下列性质的二叉查找树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度只差的绝对值不超过1。
若将二叉树结点的平衡因子定义为该节点的左子树的高度减去它的右子树的高度,则所有结点的平衡因子只可能为-1,0,1。只要有一个结点的平衡因子的绝对值大于1,那么这棵树就失去了平衡(插入和删除都会导致失衡),此时需要重新旋转最小失衡子树,将树重新变为平衡二叉树。
(1)递归找到插入的位置,生成一个新的节点插入到树中。
(2)从当前的位置开始往上查找(也就是递归返回到上一个查找的节点),更新节点的高度,判断节点是否平衡;
(3)不平衡,找到最小不平衡节点,也就找到了最小失衡子树。对最小失衡子树进行旋转,变成平衡二叉树。
(3)平衡,继续(2)步骤,直到判断完所有插入过程中经过的节点,递归结束。
//递归查找插入的位置,t是根节点、父节点、或者是最小不平衡节点,x是插入的值
template
void AvlTree::Insert(AvlNode *&t, T x)
{
if (t == NULL)//当节点为空时插入一个节点
t = new AvlNode(x);
else if (x < t->data) //向左遍历
{
Insert(t->left, x);//向左递归,直到左节点为空节点,那么就生成一个新的节点作为左节点
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{//判断平衡情况,左节点的深度-右节点的深度=平衡因子>1,表示当前节点不平衡,也就是当前节点是 最小不平衡节点
//分两种失衡情况 左左或左右
if (x < t->left->data)//左左,//插入的节点小于最小不平衡节点的左节点的值,也就是说插入的节点在最小不平衡节点的左节点的左边,右旋转
t = LL(t);
else //左右,也就是说插入的节点在最小不平衡节点的左节点的右边
t = LR(t);
}
}
else if (x > t->data) //向右遍历
{
Insert(t->right, x);//向右递归,zhi
if (GetHeight(t->right) - GetHeight(t->left) > 1)
{//找到最小不平衡节点,也就是最小失衡子树
if (x > t->right->data)//右右,插入的值大于最小不平衡节点的右节点,也就是插入的值在最小不平衡节点的右节点的右边
t = RR(t);
else
t = RL(t);
}
}
else
;//数据重复
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;//插入值路径上的所有节点高度都发生变化。计算高度 = 左右高度的最大高度+1;
}
同插入操作一样,删除结点时也有可能破坏平衡性,这就要求我们删除的时候要进行平衡性调整。
删除分为以下几种情况:
首先在整个二叉树中搜索要删除的结点,如果没搜索到直接返回不作处理,否则执行以下操作:
1.如果要删除的节点是当前根节点T。(当前根节点T,表示递归中的当前节点,而不是整个树的根节点)
如果左右子树中有一个为空(全为空NULL),那么直接用那个非空子树或者是NULL替换当前根节点即可。
如果左右子树都非空。在高度较大的子树中实施删除操作。
分两种情况:
(1)、左子树高度大于右子树高度,将左子树中最大的那个元素赋给当前根节点(后序遍历),然后删除左子树中元素值最大的那个节点。
(2)、左子树高度小于右子树高度,将右子树中最小的那个元素赋给当前根节点(中序遍历),然后删除右子树中元素值最小的那个节点。
2、如果要删除的节点元素值小于当前根节点T值,在左子树中进行删除。
(1)那么继续进行递归调用,直到在左子树中找到删除节点,找到删除节点后,执行1步骤。
(2)从当前位置开始往上查找(注:递归返回到上一个节点,也就是查找删除节点过程中经历过的所有节点),判断节点是否平衡。
(3)如果过不平衡,则节点是最小不平衡节点,找到了最小失衡子树,需要进行旋转调整,如果T的左子节点的左子树的高度大于T的左子节点的右子树的高度,进行相应的单旋转。否则进行双旋转。
(4)如果平衡,则只需要更新当前根节点T的高度信息。继续(2步骤),直到查找删除节点过程中经历过的所有节点都遍历完,递归结束。
3、要删除的节点元素值大于当前根节点T值,在右子树中进行删除。
和步骤2相同,不过递归和旋转是在右子树进行。
template
bool AvlTree::Delete(AvlNode *&t, T x)
{
//t为空 未找到要删除的结点
if (t == NULL)
return false;
//找到了要删除的结点
else if (t->data == x)
{
//左右子树都非空
if (t->left != NULL && t->right != NULL)
{//在高度更大的那个子树上进行删除操作
//左子树高度大,删除左子树中值最大的结点,将其赋给根结点
if (GetHeight(t->left) > GetHeight(t->right))
{
t->data = FindMax(t->left)->data;
Delete(t->left, t->data);
}
else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
{
t->data = FindMin(t->right)->data;
Delete(t->right, t->data);
}
}
else
{//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
AvlNode *old = t;
t = t->left ? t->left: t->right;//t赋值为不空的子结点
delete old;
}
}
else if (x < t->data)//要删除的结点在左子树上
{//找到删除节点点后的最小不平衡节点
//递归删除左子树上的结点
Delete(t->left, x);
//判断是否仍然满足平衡条件
if (GetHeight(t->right) - GetHeight(t->left) > 1)
{
if (GetHeight(t->right->left) > GetHeight(t->right->right))
{
//RL双旋转
t = RL(t);
}
else
{//RR单旋转
t = RR(t);
}
}
else//满足平衡条件 调整高度信息
{
//删除节点后,查询过的节点的高度要发生变化
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
else//要删除的结点在右子树上
{
//递归删除右子树结点
Delete(t->right, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{//找到删除节点点后的最小不平衡节点
if (GetHeight(t->left->right) > GetHeight(t->left->left))
{
//LR双旋转
t = LR(t);
}
else
{
//LL单旋转
t = LL(t);
}
}
else//满足平衡性 调整高度
{
//删除节点后,查询过的节点的高度要发生变化
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
return true;
}
平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的。
最小失衡子树:在新插入或者删除的结点向上查找,以第一个平衡因子的绝对值超过1的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的,如下。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。
最小不平衡节点 = 最小失衡子树的根节点。
解决不平衡二叉树的方法是左旋转和右旋转:
左旋(右右(后面提到的一种不平衡情况)):
//单旋转
//右右插入导致的不平衡
//t表示最小不平衡节点(最小失衡节点的根节点)
template
AvlNode * AvlTree::RR(AvlNode *t)
{
AvlNode *q = t->right;//保存当前节点的右节点
t->right = q->left;//当前节点的右节点等于当前节点的右节点的左节点
q->left = t;//当前节点的右节点的左节点等于当前节点(q就变成了根节点)
t = q;//将最小不平衡节点指向平衡后的q根节点
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
右旋转(左左):
//单旋转
//左左插入导致的不平衡
//t表示最小不平衡节点
template
AvlNode * AvlTree::LL(AvlNode *t)
{
AvlNode *q = t->left;//保存当前节点的左节点
t->left = q->right;//当前节点的左节点等于当前节点的左节点的右节点
q->right = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
https://blog.csdn.net/qq_25940921/article/details/82183093
左左(最小不平衡节点的左节点的左节点插入新节点):
右右(最小不平衡树的右节点的右节点插入新节点):
左右(最小不平衡节点的左节点的右节点插入新节点):
右左(最小不平衡节点的右节点的左节点插入新节点):
转载:https://www.cnblogs.com/zhangbaochong/p/5164994.html
所有代码:
#include
#include
using namespace std;
#pragma once
//平衡二叉树结点
template
struct AvlNode
{
T data;
int height; //结点所在高度
AvlNode *left;
AvlNode *right;
AvlNode(const T theData) : data(theData), left(NULL), right(NULL), height(0){}
};
//AvlTree
template
class AvlTree
{
public:
AvlTree(){}
~AvlTree(){}
AvlNode *root;
//插入结点
void Insert(AvlNode *&t, T x);
//删除结点
bool Delete(AvlNode *&t, T x);
//查找是否存在给定值的结点
bool Contains(AvlNode *t, const T x) const;
//中序遍历
void InorderTraversal(AvlNode *t);
//前序遍历
void PreorderTraversal(AvlNode *t);
//最小值结点
AvlNode *FindMin(AvlNode *t) const;
//最大值结点
AvlNode *FindMax(AvlNode *t) const;
private:
//求树的高度
int GetHeight(AvlNode *t);
//单旋转 左
AvlNode *LL(AvlNode *t);
//单旋转 右
AvlNode *RR(AvlNode *t);
//双旋转 右左
AvlNode *LR(AvlNode *t);
//双旋转 左右
AvlNode *RL(AvlNode *t);
};
template
AvlNode * AvlTree::FindMax(AvlNode *t) const
{
if (t == NULL)
return NULL;
if (t->right == NULL)
return t;
return FindMax(t->right);
}
template
AvlNode * AvlTree::FindMin(AvlNode *t) const
{
if (t == NULL)
return NULL;
if (t->left == NULL)
return t;
return FindMin(t->left);
}
template
int AvlTree::GetHeight(AvlNode *t)
{
if (t == NULL)
return -1;
else
return t->height;
}
//单旋转
//左左插入导致的不平衡
template
AvlNode * AvlTree::LL(AvlNode *t)
{
AvlNode *q = t->left;//保存当前节点的左节点
t->left = q->right;//当前节点的左节点等于当前节点的左节点的右节点
q->right = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
//单旋转
//右右插入导致的不平衡
template
AvlNode * AvlTree::RR(AvlNode *t)
{
AvlNode *q = t->right;//保存当前节点的右节点
t->right = q->left;//当前节点的右节点等于当前节点的右节点的左节点
q->left = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
//双旋转
//插入点位于t的左儿子的右子树
template
AvlNode * AvlTree::LR(AvlNode *t)
{
//双旋转可以通过两次单旋转实现
//对t的左结点进行RR旋转,再对根节点进行LL旋转
RR(t->left);
return LL(t);
}
//双旋转
//插入点位于t的右儿子的左子树
template
AvlNode * AvlTree::RL(AvlNode *t)
{
LL(t->right);
return RR(t);
}
//递归查找插入的位置,t是根节点、父节点、或者是最小不平衡节点,x是插入的值
template
void AvlTree::Insert(AvlNode *&t, T x)
{
if (t == NULL)//当节点为空时插入一个节点
t = new AvlNode(x);
else if (x < t->data) //向左遍历
{
Insert(t->left, x);//向左递归,直到左节点为空节点,那么就生成一个新的节点作为左节点
//判断平衡情况,左节点的深度-右节点的深度=平衡因子>1,表示当前节点不平衡,也就是当前节点是 最小不平衡节点
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{
//分两种情况 左左或左右
if (x < t->left->data)//左左,//插入的节点小于最小不平衡节点的左节点的值,也就是说插入的节点在最小不平衡节点的左节点的左边,右旋转
t = LL(t);
else //左右,也就是说插入的节点在最小不平衡节点的左节点的右边
t = LR(t);
}
}
else if (x > t->data) //向右遍历
{
Insert(t->right, x);//向右递归,zhi
if (GetHeight(t->right) - GetHeight(t->left) > 1)//找到最小不平衡节点
{
if (x > t->right->data)//右右,插入的值大于最小不平衡节点的右节点,也就是插入的值在最小不平衡节点的右节点的右边
t = RR(t);
else
t = RL(t);
}
}
else
;//数据重复
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;//插入值路径上的所有节点高度都发生变化。计算高度 = 左右高度的最大高度+1;
}
template
bool AvlTree::Delete(AvlNode *&t, T x)
{
//t为空 未找到要删除的结点
if (t == NULL)
return false;
//找到了要删除的结点
else if (t->data == x)
{
//左右子树都非空
if (t->left != NULL && t->right != NULL)
{//在高度更大的那个子树上进行删除操作
//左子树高度大,删除左子树中值最大的结点,将其赋给根结点
if (GetHeight(t->left) > GetHeight(t->right))
{
t->data = FindMax(t->left)->data;
Delete(t->left, t->data);
}
else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
{
t->data = FindMin(t->right)->data;
Delete(t->right, t->data);
}
}
else
{//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
AvlNode *old = t;
t = t->left ? t->left: t->right;//t赋值为不空的子结点
delete old;
}
}
else if (x < t->data)//要删除的结点在左子树上
{//找到删除节点点后的最小不平衡节点
//递归删除左子树上的结点
Delete(t->left, x);
//判断是否仍然满足平衡条件
if (GetHeight(t->right) - GetHeight(t->left) > 1)
{
if (GetHeight(t->right->left) > GetHeight(t->right->right))
{
//RL双旋转
t = RL(t);
}
else
{//RR单旋转
t = RR(t);
}
}
else//满足平衡条件 调整高度信息
{
//删除节点后,查询过的节点的高度要发生变化
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
else//要删除的结点在右子树上
{
//递归删除右子树结点
Delete(t->right, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{//找到删除节点点后的最小不平衡节点
if (GetHeight(t->left->right) > GetHeight(t->left->left))
{
//LR双旋转
t = LR(t);
}
else
{
//LL单旋转
t = LL(t);
}
}
else//满足平衡性 调整高度
{
//删除节点后,查询过的节点的高度要发生变化
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
return true;
}
//查找结点
template
bool AvlTree::Contains(AvlNode *t, const T x) const
{
if (t == NULL)
return false;
if (x < t->data)
return Contains(t->left, x);
else if (x > t->data)
return Contains(t->right, x);
else
return true;
}
//中序遍历
template
void AvlTree::InorderTraversal(AvlNode *t)
{
if (t)
{
InorderTraversal(t->left);
cout << t->data << ' ';
InorderTraversal(t->right);
}
}
//前序遍历
template
void AvlTree::PreorderTraversal(AvlNode *t)
{
if (t)
{
cout << t->data << ' ';
PreorderTraversal(t->left);
PreorderTraversal(t->right);
}
}
#include "AvlTree.h"
int main()
{
AvlTree tree;
int value;
int tmp;
cout << "请输入整数建立二叉树(-1结束):" << endl;
while (cin >> value)
{
if (value == -1)
break;
tree.Insert(tree.root,value);
}
cout << "中序遍历";
tree.InorderTraversal(tree.root);
cout << "\n前序遍历:";
tree.PreorderTraversal(tree.root);
cout << "\n请输入要查找的结点:";
cin >> tmp;
if (tree.Contains(tree.root, tmp))
cout << "已查找到" << endl;
else
cout << "值为" << tmp << "的结点不存在" << endl;
cout << "请输入要删除的结点:";
cin >> tmp;
tree.Delete(tree.root, tmp);
cout << "删除后的中序遍历:";
tree.InorderTraversal(tree.root);
cout << "\n删除后的前序遍历:";
tree.PreorderTraversal(tree.root);
}