平衡二叉树—AVL树与红黑树(AVL树已实现)
1.平衡二叉树
平衡二叉树:指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。
2.平衡二叉树示意图
第一棵树左边节点深度为3右边节点深度为1这样高度差为2这样的就是不平衡的;
第二棵树左边节点深度为2右边节点深度为1这样高度差为1这样的就是平衡的;
如果不平衡的话要咋作呢
比如一棵树上有ABCDEF,6个结点的树
如图
这样一棵不平衡二叉树
要咋做呢
首先我们先分清不平衡二叉树有哪些类型
LL类型
RR类型
LR类型
RL类型
在这里我们的是LL类型
那么要咋做能
一般是旋转
LL类型是右旋
RR类型是左旋
LR类型是先作左旋再右旋
RL类型是先作右旋再左旋
我们的树右旋后
代码的话会在下面AVL树中实现;
(那些好看的图片来源https://blog.youkuaiyun.com/qq_25343557/article/details/89110319)
AVL树
1.什么是AVL树
1.在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。
2.增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
2.实现
右旋
void LL(node *&t){
//左边节点这节点会变成根节点
node *t1 = t->left;
//让传进来t改变左边节点变为t1的右节点因为他比t1大又比t里面的数据小
t->left = t1->right;
//改变根节点
t1->right = t;
//更新两边节点深度
t->height = max(high(t->left), high(t->right)) + 1;
t1->height = max(high(t1->left), high(t1->right)) + 1;
t = t1;
}
左旋
//原理和右旋一样
void RR(node *&t){
node *t1 = t->right;
t->right = t1->left;
t1->left = t;
t->height = max(high(t->left), high(t->right)) + 1;
t1->height = max(high(t1->left), high(t1->right)) + 1;
t = t1;
}
总代码
#include <iostream>
using namespace std;
template <class elemType>
class AvlTree{
private:
//结构体定义节点
struct node{
elemType data;//用于存数据
//左右节点
node *left;
node *right;
int height;//节点深度
//成员列表初始化
node(const elemType &x, node *ln, node *rn, int h = 1) :data(x), left(ln), right(rn), height(h){}
};
//定义根节点
node *root;
public:
AvlTree(){
root = NULL;
}
~AvlTree(){
clear(root);
}
//用于删除树
void clear(node *t){
if (t == NULL){
return;
}
clear(t->left);
clear(t->right);
delete t;
}
//寻找数据
elemType *find(const elemType &x){
node *t = root;
while (t != NULL && t->data != x){
//根据二叉树的性质比自身数据大的节点放在右边那么我们就去右边找
if (t->data > x){
t = t->right;
}
//反之去左边
else{
t = t->left;
}
}
//找不到返回空
if (t == NULL){
return NULL;
}
else{
return &(t->data);
}
}
//插入数据
//因为在类外无法访问道私有成员
//所以我们定义一个函数来访问私有成员
void insert(const elemType &x){
insert(x, root);
}
//这里用的递归法
void insert(const elemType &x, node *&t){
//这时有两种情况
//一种是根节点为空也就是没有建立树还有到了可以插入数据的时候
//第二种是有根节点寻找插入数据的时候
if (t == NULL){
t = new node(x, NULL, NULL);
}
else{
//根据二叉树的性质大的放右子节点小的放左子节点
if (x < t->data){
insert(x, t->left);
//这里判断左边的深度是否大右边节点深度超过1;
//因为每次插入都要判断所以我们不用考虑多个LL或RR等情况
if (high(t->left) - high(t->right) == 2){
//这里是判断是LL型的还是LR型的
//因为我们那个high(t->left) - high(t->right) == 2只能判断是否左边失衡
if (x < t->left->data){
LL(t);
}
else{
LR(t);
}
}
}
else{
//这里的原理和上面的一样
if (x == t->data){
cout << "The node has existed" << endl;
return;
}
else{
insert(x, t->right);
if (high(t->right) - high(t->left) == 2){
if (x > t->right->data){
RR(t);
}
else{
RL(t);
}
}
}
}
}
//每次都要更新节点深度因为插入了新的几点根节点一定要变得
//这里会递归回根节点;
t->height = max(high(t->left), high(t->right)) + 1;
}
//移除也是bug所在。
//但是思路很清晰
//这里也是因为外面调不道私有数据成员才写的函数
void remove(const elemType &x){
remove(x, root);
}
void remove(const elemType &x, node *&t){
//这里我说一下思路
//要删除数据一定要先找到数据然后看有没有左右节点然后进分情况讨论
//1第一就是都有的情况
//2第二就是只有左节点或右节点
//3第三种是都没有
//3还有删除后还要考虑时候还是个平衡二叉树
//这里每个remove()的下面的代码都是用来保证第三条的
//在这里我想应该是采用和右节点最后一个左节点的数据到最后一个节点再删除的方法
//然后在利用递归来查看是否还是平衡二叉树;
if (t == NULL){
return;
}
if (x < t->data){
remove(x, t->left);
if (high(t->right) - high(t->left) == 2){
if (t->right->left != NULL && high(t->right->left) > high(t->right->right)){
RL(t);
}
else{
RR(t);
}
}
}
else{
if (x > t->data){
remove(x, t->right);
if (high(t->left) - high(t->right) == 2){
if (t->left->right != NULL && high(t->left->right) > high(t->left->left)){
LR(t);
}
else{
LL(t);
}
}
}
else{
if (t->left != NULL && t->right != NULL){
node *tmp = t->right;
while (tmp->left != NULL){
tmp = tmp->left;
}
t->data = tmp->data;
remove(t->data, t->right);
if (high(t->left) - high(t->right) == 2){
if (t->left->right != NULL && high(t->left->right) > high(t->left->left)){
LR(t);
}
else{
LL(t);
}
}
}
else{
node *old = t;
if (t->left == NULL && t->right == NULL){
delete old;
}
else{
if (t->left != NULL){
t = t->left;
}
else{
t = t->right;
}
delete old;
}
}
}
}
t->height = max(high(t->left), high(t->right)) + 1;
}
//得到深度这个值返回有两个作用一是更新节点深度二是判断左右节点深度差是否超过1
int high(node *t){
if (t == NULL){
return 0;
}
else{
return t->height;
}
}
//LL型右旋
void LL(node *&t){
node *t1 = t->left;
t->left = t1->right;
t1->right = t;
t->height = max(high(t->left), high(t->right)) + 1;
t1->height = max(high(t1->left), high(t1->right)) + 1;
t = t1;
}
//LR型先左旋再右旋
void LR(node *&t){
RR(t->left);
LL(t);
}
//RR型左旋
void RR(node *&t){
node *t1 = t->right;
t->right = t1->left;
t1->left = t;
t->height = max(high(t->left), high(t->right)) + 1;
t1->height = max(high(t1->left), high(t1->right)) + 1;
t = t1;
}
//RL型先右旋再左旋
void RL(node *&t){
LL(t->right);
RR(t);
}
//这里是得到左右两个节点深度的最大值用于更新节点的深度
int max(int a, int b){
if (a > b){
return a;
}
else{
return b;
}
}
//中序遍历
void midOrder(){
midOrder(root);
}
void midOrder(node *p){
if (p == NULL){
return;
}
midOrder(p->left);
cout << p->data << ' ';
midOrder(p->right);
}
(代码来源https://blog.youkuaiyun.com/qq_39747794/article/details/83654985?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
)
这个代码虽然有bug但是我个人认为最好理解的了(虽然大家写的都差不多。。。还有这注释是我加的)
//这里是我修改后的
//上面bug在移除后还在使用那个空间使其父节点深度没有改变
void Tree::Remove(int data, Node* &node)
{
if (node == nullptr)
{
return;
}
if (data > node->Data)
{
Remove(data, node->Right);
if (GetHight(node->Left) - GetHight(node->Right) == 2)
{
if (node->Left->Right != nullptr&&GetHight(node->Left->Right) > GetHight(node->Left->Left))
{
LR(node);
}
else
{
LL(node);
}
}
}
else if (data < node->Data)
{
Remove(data, node->Left);
if (GetHight(node->Right) - GetHight(node->Left) == 2)
{
if (node->Right->Left != nullptr && GetHight(node->Right->Left)>GetHight(node->Right->Right))
{
RL(node);
}
else
{
RR(node);
}
}
}
else
{
if (node->Left == nullptr && node->Right == nullptr)
{
delete node;
node = nullptr;
return;
}
else if (node->Left != nullptr && node->Right != nullptr)
{
Node* temp = node->Right;
while (temp->Left != nullptr)
{
temp = temp->Left;
}
node->Data = temp->Data;
Remove(node->Data, node->Right);
if (GetHight(node->Left) - GetHight(node->Right) == 2)
{
if (node->Left->Right != nullptr&&GetHight(node->Left->Right) > GetHight(node->Left->Left))
{
LR(node);
}
else
{
LL(node);
}
}
}
else
{
Node* temp = node;
if (node->Right != nullptr)
{
node = node->Right;
}
if (node->Left != nullptr)
{
node = node->Left;
}
delete temp;
return;
}
}
node->Hight = MAX(GetHight(node->Left), GetHight(node->Right)) + 1;
}
红黑树
1,红黑树是什么
红黑树:一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
红黑树的主要特性:
(1)每个节点要么是黑色,要么是红色。(节点非黑即红)
(2)根节点必须是黑色。
(3)每个叶子节点(Nuil)是黑色。
(4)如果一个节点是红色的,则它的子节点必须是黑色的。(也就是说父子节点不能同时为红色)
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。(这一点是平衡的关键)
2.实现
可以看看https://www.cnblogs.com/WindSun/p/10890505.html这个有注释;