AVL平衡二叉树

前言:这里学习和实现下相关平衡二叉树,之前windows内核中就有看到AVL树,那时候也只会看到这个结构体,但是具体的实现觉得自己没必要了解,但是在学习数据结构的时候又发现了这个,那自己想反正内核也有,那自己就一起好好学习实现下

参考文章:https://zhuanlan.zhihu.com/p/56066942

Windows采用平衡二叉树把这些离散的地址范围管理起来。

在32位Windows系统中,进程在用户态可用的地址空间范围是低2G(x64下是低8192G)。随着进程不断的申请和释放内存,这个2G的地址空间,有的地址范围是保留状态(reserved),有的地址范围是提交状态(映射到了物理页面,committed),有的地址范围是空闲的。

平衡二叉树的定义

平衡二叉查找树:简称平衡二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:

  • 可以是空树。

  • 假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过 1。

它的定义任意一个结点的左右子树的高度相差不超过1的树为平衡二叉树。

例如下面的图不是平衡二叉树,因为结点 60 的左子树不是平衡二叉树。

下面的图也不是二叉树,因为虽然任何一个结点的左子树与右子树都是平衡二叉树,但高度之差已经超过1。

什么是平衡因子

定义:某节点的左子树与右子树的高度(深度)差即为该节点的平衡因子(BF,Balance Factor)。

平衡二叉树中不存在平衡因子大于 1 的节点。

在一棵平衡二叉树中,节点的平衡因子只能取 0 、1 或者 -1 ,分别对应着左右子树等高,左子树比较高,右子树比较高,其实1和-1都一样,只是右子树比左子树高了1个高度,反过来其实都是一样的

如何理解这个平衡因子,比如如下图所示,这颗树的左右结点的高度都是N,那么它们差就是0,而平衡因子就是左子树的高度减去右子树的高度的一个数值

平衡因子为0的情况如下图所示,那么就说明当前节点的左右子树高度差为0

平衡因子为1的情况如下图所示,那么就说明当前节点的左右子树高度差为1

最小失衡子树

最小失衡子树的定义就是在新插入的结点向上查找,以第一个平衡因子的绝对值超过 1 的结点为根的子树称为最小不平衡子树。

简单的理解在一棵平衡二叉树中插入一个节点,插入了之后导致当前的树不再处于平衡的状态,也就是平衡因子超过1,那么以当前节点向上走,从当前节点到平衡因子超过1的节点之间的节点就被称作为

左旋LL LR / 右旋 RR RL

平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的。根据旋转的方向有两种处理方式,左旋与右旋。

这里的话可以把左旋和右旋这种方式理解为修复平衡的手段,当我们擅自插入了一个结点之后,可能就会导致当前树不再是平衡树,而通过相关的操作(左旋和右旋)能够将其平衡,具体就是修复其最小平衡二叉树,让其整个树来达到平衡的效果。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
typedef struct _BTreeNode{
struct _BTreeNode* pLeftBtreeNode;
struct _BTreeNode* pRightBtreeNode;
ElemType height;
ElemType nodeData;
}BTreeNode, *PBTreenode;
typedef struct _SqTree{
BTreeNode* pRootTreeNode;
}SqTree, *PSqTree;
// init root tree node
Status initRootTreeNode(SqTree* pSqTree, ElemType elem)
{
if (pSqTree->pRootTreeNode != NULL)
return ERROR;
pSqTree->pRootTreeNode = malloc(sizeof(BTreeNode));
memset(pSqTree->pRootTreeNode, 0, sizeof(BTreeNode));
pSqTree->pRootTreeNode->nodeData = elem;
if (pSqTree->pRootTreeNode != NULL)
return OK;
else
return ERROR;
}
// 平衡 - LL旋转类型
void LLolation()
{
}
// 平衡 - RR旋转类型
void RRotation()
{
}
// 平衡 - RL旋转类型
void RLotation()
{
}
// 平衡 - LR旋转类型
void LRotation()
{
}
// 二叉排序树的结点插入
// 在插入的过程中同样实现保持平衡的功能
// 一共有四种情况,LL / RR / LR / RL
// 不用这种方式写了,我不知道该怎么写,我换了一种方法写,因为如果我这样写的话它会造成一种很混乱的现象
/*
void insertBTreeNode(BTreeNode* pBTreeNode, BTreeNode* pPreBTreeNode, ElemType elem)
{
// 如果为空的话,那么就要插入到pBTreeNode左节点或者右节点了
BTreeNode* pNewBTreeNode = NULL;
if (pBTreeNode == NULL)
{
if (pNewBTreeNode == NULL)
{
pNewBTreeNode = malloc(sizeof(BTreeNode));
memset(pNewBTreeNode, 0, sizeof(BTreeNode));
pNewBTreeNode->nodeData = elem;
if (pPreBTreeNode->nodeData > elem)
pPreBTreeNode->pLeftBtreeNode = pNewBTreeNode;
else if (pPreBTreeNode->nodeData < elem)
pPreBTreeNode->pRightBtreeNode = pNewBTreeNode;
}
}
else if (pBTreeNode->nodeData < elem)
{
// 如果是大的话,那么传入到右子树中
pPreBTreeNode = pBTreeNode;
insertBTreeNode(pBTreeNode->pRightBtreeNode, pPreBTreeNode, elem);
if (pPreBTreeNode->nodeData > elem)
{
}
}
else if (pBTreeNode->nodeData > elem)
{
// 如果是小的话,那么传入到左节点中
pPreBTreeNode = pBTreeNode;
insertBTreeNode(pBTreeNode->pLeftBtreeNode, pPreBTreeNode, elem);
}
pNewBTreeNode->height = getMax(getHeight(pNewBTreeNode->pLeftBtreeNode), getHeight((pNewBTreeNode)->pRightBtreeNode)) + 1;
}*/
// 获取高度
ElemType getNodeHeight(BTreeNode* pTreeNode)
{
return pTreeNode ? pTreeNode->height : 0;
}
void insertBTreeNode2(BTreeNode** pTreeNode, ElemType elem)
{
// 如果*pTreeNode为空的话,那么就初始化,因为为空的话那么就意味着是要被新增的节点
if (*pTreeNode == NULL)
{
*pTreeNode = malloc(sizeof(BTreeNode));
if (*pTreeNode == NULL)
return;
(*pTreeNode)->height = 0;
(*pTreeNode)->nodeData = elem;
(*pTreeNode)->pLeftBtreeNode = NULL;
(*pTreeNode)->pRightBtreeNode = NULL;
}
// 如果比父节点小的话,那么则插入到左节点中,可以确认的是当前的位置是位于根节点的左侧
if ((*pTreeNode)->nodeData > elem)
{
insertBTreeNode2(&(*pTreeNode)->pLeftBtreeNode, elem);
// 来判断当前的节点是否处于平衡的情况
ElemType iRightHeight = getNodeHeight((*pTreeNode)->pRightBtreeNode);
ElemType iLeftHeight = getNodeHeight((*pTreeNode)->pLeftBtreeNode);
// 可能是LL或者LR
if (iLeftHeight - iRightHeight == 2)
{
// 如果是LL的话,那么当前节点的左节点的nodeData会大于elem
if ((*pTreeNode)->pLeftBtreeNode->nodeData > elem)
{
// LL旋转 -> 失衡节点在当前节点的左节点的左节点
BTreeNode* pTemp = (*pTreeNode)->pLeftBtreeNode;
(*pTreeNode)->pLeftBtreeNode = pTemp->pRightBtreeNode;
pTemp->pRightBtreeNode = (*pTreeNode);
/*
6 6
5 7 3 7
(3) -> 2 5
2 4 4
*/
// 旋转平衡之后,还需要修正当前节点左右子树的高度
(*pTreeNode)->height = max(getNodeHeight((*pTreeNode)->pLeftBtreeNode), getNodeHeight((*pTreeNode)->pRightBtreeNode)) + 1;
pTemp->height = max(getNodeHeight(pTemp->pLeftBtreeNode), getNodeHeight(pTemp->pRightBtreeNode)) + 1;
// 根节点变换
*pTreeNode = pTemp;
}
else if ((*pTreeNode)->pLeftBtreeNode->nodeData < elem)
{
// 如果是LR的话,那么当前节点的左节点的nodeData会小于elem
// LR旋转 -> 失衡节点在当前节点的左节点的右节点
}
}
}
else if ((*pTreeNode)->nodeData < elem)
{
insertBTreeNode2(&(*pTreeNode)->pRightBtreeNode, elem);
// 来判断当前的节点是否处于平衡的情况
ElemType iRightHeight = getNodeHeight((*pTreeNode)->pRightBtreeNode);
ElemType iLeftHeight = getNodeHeight((*pTreeNode)->pLeftBtreeNode);
// 可能是RR或者RL
if (iRightHeight - iLeftHeight == 2)
{
// 如果是RR的话,那么当前节点的右节点的nodeData会大于elem
// RR旋转 -> 失衡节点在当前节点的右节点的右节点
if ((*pTreeNode)->pRightBtreeNode->nodeData < elem)
{
BTreeNode* pTemp = (*pTreeNode)->pRightBtreeNode;
(*pTreeNode)->pRightBtreeNode = pTemp->pLeftBtreeNode;
pTemp->pLeftBtreeNode = *pTreeNode;
/*
2 2
1 (4) 1 6
6 -> 4 8
5 8 5
*/
// 旋转平衡之后,还需要修正当前节点左右子树的高度
(*pTreeNode)->height = max(getNodeHeight((*pTreeNode)->pLeftBtreeNode), getNodeHeight((*pTreeNode)->pRightBtreeNode)) + 1;
pTemp->height = max(getNodeHeight(pTemp->pLeftBtreeNode), getNodeHeight(pTemp->pRightBtreeNode));
// 根节点变换
*pTreeNode = pTemp;
}
else if ((*pTreeNode)->pRightBtreeNode->nodeData > elem)
{
// 如果是RL的话,那么当前节点的右节点的nodeData会大于elem
// RL旋转 -> 失衡节点在当前节点的右节点的左节点
BTreeNode* pTemp = (*pTreeNode)->pRightBtreeNode;
}
}
}
// 每次都要变化当前节点的高度,为后面平衡二叉树做准备
(*pTreeNode)->height = max(getNodeHeight((*pTreeNode)->pLeftBtreeNode), getNodeHeight((*pTreeNode)->pRightBtreeNode)) + 1;
}
void preOrder(BTreeNode* pBTreeNode)
{
if (pBTreeNode != NULL)
{
printf("%d ", pBTreeNode->nodeData);
preOrder(pBTreeNode->pLeftBtreeNode);
preOrder(pBTreeNode->pRightBtreeNode);
}
}
int main()
{
SqTree sqTree;
memset(&sqTree, 0, sizeof(SqTree));
// initRootTreeNode(&sqTree, 1);
int a[6] = { 6, 5, 3, 2, 4, 7 }; // LL类型
for (int i = 0;i<6;i++)
insertBTreeNode2(&sqTree.pRootTreeNode, a[i]);
preOrder(sqTree.pRootTreeNode);
return 0;
}
posted @   zpchcbd  阅读(98)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2020-04-04 Thinkphp 5.0.x 未开启强制路由导致的RCE 漏洞分析
点击右上角即可分享
微信分享提示