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 @ 2022-04-04 15:19  zpchcbd  阅读(131)  评论(0)    收藏  举报