目录
1、平衡二叉树
2、平衡操作
谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注
没错,说的就是你,不用再怀疑!!!
希望我的文章内容能对你有帮助,一起努力吧!!!
1、平衡二叉树
平衡二叉树(Balance Binary Tree):又称AVL树
它或者是一棵空树,或者是具有一下性质:
它的左子树和右子树本身又是一棵平衡二叉树,且左子树和右子树的深度之差的绝对值不会超过1
若平衡二叉树的结点的平衡因子定义为:
- 该结点的左子树深度减去右子树深度
- 则平衡二叉树上所有的平衡因子取值范围:-1 0 1
- 只要有一个结点的平衡因子的绝对值是大于1的,那么这颗树就不是平衡的。
平衡二叉树通过平衡的二叉树排序树来实现的
2、平衡操作
场景:左深左插:由于在左子树的左边增加一个新结点,导致当前结点的平衡因子大于1了
- 单向右旋平衡处理
场景:右深右插:由于在右子树的右边增加一个新结点,导致当前结点的平衡因子大于1了
- 单向左旋平衡处理
***示例代码***
#include <iostream>
#include <queue>
#define MAXLENGTH(l,r) (l>r?l:r)
// 树结点类型
typedef struct treenode
{
int value;
int height;
struct treenode *lchild;
struct treenode *rchild;
}Tree;
int getHeight(Tree *tree)
{
if(!tree)
return 0;
return tree->height;
}
int getBalance(Tree *lchild,Tree *rchild)
{
return getHeight(lchild) - getHeight(rchild);
}
/*
@brief 增加新的结点进入二叉树中(循环法)
@param tree 需要增加结点的树的根结点指针
@param data 需要增加进树的新结点数据
@return 返回树的根结点
*/
Tree *addNewNodeLoop(Tree *tree,int data)
{
// 如果树为空作为根结点插入
if(tree == nullptr)
{
Tree *node_ptr = new Tree;
node_ptr->value = data;
node_ptr->lchild = nullptr;
node_ptr->rchild = nullptr;
return node_ptr;
}
// 如果树不为空的时候,需要和根结点进行比较
Tree *father = nullptr; // 父结点
Tree *current_node = tree; // 当前结点
while(current_node)
{
father = current_node; // 将当前结点作为下一个结点的父结点
// 当前结点的数据大于data,需要往左查
if(current_node->value > data)
current_node = current_node->lchild; // 将当前结点的左子结点作为新的根结点来继续下次比较
// 当前结点的数据小于data,需要往右查
else if(current_node->value < data)
current_node = current_node->rchild; // 将当前结点的右子结点作为新的根结点来继续下次比较
else
return tree; // 不能存在相等情况,所以直接返回
}
// 出来循环:找到了插入的位置
current_node = new Tree;
current_node->value = data;
current_node->lchild = nullptr;
current_node->rchild = nullptr;
// 确定是左插还是右插
if(father->value > data)
// 左插
father->lchild = current_node;
else
// 右插
father->rchild = current_node;
return tree;
}
/*
@brief 单向右旋平衡处理:左深左插
@param k2 需要进行单向右旋平衡处理的结点
@return 平衡处理完成之后,返回新的结点指针
*/
Tree *SingleRotateWidthRight(Tree *k2)
{
// 获取k2的左子结点
Tree *k1 = k2->lchild;
// k2的lchild指向k1的rchild
k2->lchild = k1->rchild;
// k1的rchild指向k2
k1->rchild = k2;
// 更新高度
k2->height = MAXLENGTH(getHeight(k2->lchild),getHeight(k2->rchild))+1;
k1->height = MAXLENGTH(getHeight(k1->lchild),getHeight(k1->rchild))+1;
// 返回k1
return k1;
}
/*
@brief 单向左旋平衡处理:右深右插
@param k2 需要进行单向左旋平衡处理的结点
@return 平衡处理完成之后,返回新的结点指针
*/
Tree *SingleRotateWidthLeft(Tree *k2)
{
// 获取k2的右子结点
Tree *k1 = k2->rchild;
// k2的rchild指向k1的lchild
k2->rchild = k1->lchild;
// k1的lchild指向k2
k1->lchild = k2;
// 更新高度
k2->height = MAXLENGTH(getHeight(k2->lchild),getHeight(k2->rchild))+1;
k1->height = MAXLENGTH(getHeight(k1->lchild),getHeight(k1->rchild))+1;
// 返回k1
return k1;
}
/*
@brief 双向旋转 - 先左后右
@param k3 需要进行双向旋转平衡处理的结点
@return 平衡处理完成之后,返回新的结点指针
*/
Tree *DoubleRotateLeftRight(Tree *k3)
{
// 将k2进行左旋处理
k3->lchild = SingleRotateWidthLeft(k3->lchild);
// 上述操作:目的为了把k3从左深右插变成左深左插
// 将k3进行右旋处理
k3 = SingleRotateWidthRight(k3);
return k3;
}
/*
@brief 双向旋转 - 先右后左
@param k3 需要进行双向旋转平衡处理的结点
@return 平衡处理完成之后,返回新的结点指针
*/
Tree *DoubleRotateRightLeft(Tree *k3)
{
// 将k2进行左旋处理
k3->rchild = SingleRotateWidthRight(k3->rchild);
// 上述操作:目的为了把k3从左深右插变成左深左插
// 将k3进行右旋处理
k3 = SingleRotateWidthLeft(k3);
return k3;
}
/*
@brief 增加新的结点进入二叉树中(递归法)
@param tree 需要增加结点的树的根结点指针
@param data 需要增加进树的新结点数据
@return 返回树的根结点
*/
Tree *addNewNode(Tree *tree,int data)
{
// 如果树为空作为根结点插入
if(tree == nullptr)
{
Tree *node_ptr = new Tree;
node_ptr->value = data;
node_ptr->height = 1; // 它本身的高度
node_ptr->lchild = nullptr;
node_ptr->rchild = nullptr;
return node_ptr;
}
// 如果树不为空的时候,需要和根结点进行比较
if(tree->value > data)
// 左插
tree->lchild = addNewNode(tree->lchild,data);
else if(tree->value < data)
// 右插
tree->rchild = addNewNode(tree->rchild,data);
//------------------------------------------------------------------------------------------------
// 更新高度:不管新结点有没有增加成功,都更新一下当前结点高度
tree->height = MAXLENGTH(getHeight(tree->lchild),getHeight(tree->rchild)) + 1;
// 计算平衡因子,来判断是否失衡
// 先获取该节点的平衡因子
int balance_dValue = getBalance(tree->lchild,tree->rchild);
// 判断是否失衡
if(abs(balance_dValue) > 1)
{
// 判断左右子树谁引起失衡
if(balance_dValue > 0) // 左子树失衡:说明一个问题,左子树比右子树高:左深
{
// 判断是左插还是右插
if(tree->lchild->value > data) // 左插
{
// 左深左插:单向右旋处理
tree = SingleRotateWidthRight(tree); // tree的指向会发生改变
}
else // 右插
{
tree = DoubleRotateLeftRight(tree);
}
}
else// 右子树失衡:说明一个问题,右子树比左子树高:右深
{
// 判断是左插还是右插
if(tree->rchild->value < data) // 右插
{
std::cout << "右深插" << std::endl;
// 右深右插:单向右旋处理
tree = SingleRotateWidthLeft(tree); // tree的指向会发生改变
}
else // 左插
{
tree = DoubleRotateRightLeft(tree);
}
}
}
return tree;
}
/*
@brief 创建一棵二叉排序树
@return 成功返回创建好的树的首地址
*/
Tree *createNewTree()
{
// 一棵空树
Tree *tree = nullptr;
while(1)
{
int data = -1;
std::cin >> data;
if(data == -1)
break;
// 插入到树中
tree = addNewNode(tree,data);
}
// 返回创建好的树
return tree;
}
/*
@brief 先序遍历二叉树的结点 根左右
@param tree 需要先序遍历的二叉树根结点指针
*/
void frontPrintTree(Tree *tree)
{
// 判断一下根结点是否为空
if(tree == nullptr)
return;
// 把传入的结点直接作为根结点使用
// 打印根结点
std::cout << tree->value;
// 打印左子树:这里的tree->lchild其实就是左子树的根
frontPrintTree(tree->lchild);
// 打印右子树:这里的tree->rchild其实就是右子树的根
frontPrintTree(tree->rchild);
}
/*
@brief 中序遍历二叉树的结点 左根右
@param tree 需要先序遍历的二叉树根结点指针
*/
void middlePrintTree(Tree *tree)
{
// 判断一下根结点是否为空
if(tree == nullptr)
return;
// 把传入的结点直接作为根结点使用
// 打印左子树:这里的tree->lchild其实就是左子树的根
middlePrintTree(tree->lchild);
// 打印根结点
std::cout << tree->value;
// 打印右子树:这里的tree->rchild其实就是右子树的根
middlePrintTree(tree->rchild);
}
/*
@brief 后序遍历二叉树的结点 左右根
@param tree 需要先序遍历的二叉树根结点指针
*/
void backPrintTree(Tree *tree)
{
// 判断一下根结点是否为空
if(tree == nullptr)
return;
// 把传入的结点直接作为根结点使用
// 打印左子树:这里的tree->lchild其实就是左子树的根
backPrintTree(tree->lchild);
// 打印右子树:这里的tree->rchild其实就是右子树的根
backPrintTree(tree->rchild);
// 打印根结点
std::cout << tree->value;
}
int main()
{
Tree *tree = createNewTree();
frontPrintTree(tree);
return 0;
}