AVL树详解

AVLTree树

AVL树的名字来源于它的发明作者G.M. Adelson-Velsky 和 E.M. Landis。AVL树是最先发明的自平衡二叉查找树(Self-Balancing Binary Search Tree,简称平衡二叉树)。

  • 定义

1、必须是一颗二插搜索树
2、每个节点的左右子树高度差最多为1

  • 一些概念:

平衡因子:节点左子树的高度减去其右子树的高度成为该节点的平衡因子(BF)
最小不平衡树:距离插入节点最近的,且左右平衡因子的绝对值大于1的节点

下图中在节点5右子树插入10和18后,节点5就变成了最小不平衡树。

  • 失衡调整

为了维持平衡的性质,需要对二叉树进行左旋、右旋、左右旋、右左旋四种情况的调整。

标记不同颜色的点为最小失衡树根节点

(1)左旋

发生的情景:当在右子树的右子树上插入节点

插入操作的时候节点10是没有左孩子的,要是有左孩子了,那就会发生先右旋再左旋的情况。删除的时候可能会出现有左孩子的情况

故在编码的时候节点10的左孩子需要考虑进去。其他的几种旋转等同。

 

(2)右旋

发生的情景:当在左子树的左子树上插入节点

(3)先左旋再右旋

发生的情景:在左子树的右子树上插入节点

(4)先右旋再左旋

发生的情景:在右子树的左子树上插入节点

 

//AVLTree.h
#pragma once
#include <algorithm>
#include <iostream>
/*
AVLTree性质
1、必须是一颗二插搜索树
2、每个节点的左右子树高度差最多为1
一些概念:
平衡因子:节点左子树的高度减去其右子树的高度成为该节点的平衡因子(BF)
最小不平衡树:距离插入节点最近的,且左右平衡因子的绝对值大于1的节点
*/

template <typename T>
struct AVLTreeNode
{
	T data;
	int height;//定义树的高度
	AVLTreeNode<T> *pLeftChild;
	AVLTreeNode<T> *pRightChild;

	AVLTreeNode(T data, AVLTreeNode<T> *pLeft, AVLTreeNode<T> *pRight):
		data(data), pLeftChild(pLeft), pRightChild(pRight)
	{}
};

template <typename T>
class AVLTree
{
public:
	AVLTree()
	{
		pRoot = nullptr;
	}
	~AVLTree()
	{
		Destroy();
	}

public:
	void Insert(T data);//插入、创建
	void PreTraver();//遍历
	void Delete(T data);//删除
	AVLTreeNode<T>* Search(T data);//查找
	void Destroy();

private:
	//获取该节点的树高度
	int GetHeight(AVLTreeNode<T> *pNode);

	//增加树的节点,需根据节点高度进行左旋、右旋等操作
	void InsertNode(AVLTreeNode<T> *&pNode, T value);

	//左旋
	void LeftRotate(AVLTreeNode<T> *&pNode);

	//右旋
	void RightRotate(AVLTreeNode<T> *&pNode);

	//先左旋再右旋
	void LeftRightRotate(AVLTreeNode<T> *&pNode);

	//先右旋再左旋
	void RightLeftRotate(AVLTreeNode<T> *&pNode);
	//删
	void Delete(AVLTreeNode<T> *&pNode, T data);
	//查
	AVLTreeNode<T> * SearchNode(AVLTreeNode<T> *pNode, T data);

	//前序遍历
	void PreTraver(AVLTreeNode<T> *pNode);

	//获取最大节点
	AVLTreeNode<T> *GetMaxNode(AVLTreeNode<T> *pNode);
	//获取最小节点
	AVLTreeNode<T> *GetMinNode(AVLTreeNode<T> *pNode);
	//销毁二叉树
	void Destroy(AVLTreeNode<T> *pNode);

private:
	AVLTreeNode<T> *pRoot;
};

template<typename T>
void AVLTree<T>::Insert(T data)
{
	InsertNode(pRoot, data);
}

template<typename T>
void AVLTree<T>::PreTraver()
{
	PreTraver(pRoot);
}

template<typename T>
void AVLTree<T>::Delete(T data)
{
	Delete(pRoot, data);
}

template<typename T>
AVLTreeNode<T>* AVLTree<T>::Search(T data)
{
	return SearchNode(pRoot, data);
}

template<typename T>
inline void AVLTree<T>::Destroy()
{
}

template<typename T>
int AVLTree<T>::GetHeight(AVLTreeNode<T> * pNode)
{
	return (pNode == nullptr) ? 0 : pNode->height;
}

/*
1、插入应该满足二插搜索树的性质
2、高度大于1时需根据情况进行旋转
*/
template<typename T>
void AVLTree<T>::InsertNode(AVLTreeNode<T> *&pNode, T value)
{
	if (pNode == nullptr)
		pNode = new AVLTreeNode<T>(value, nullptr, nullptr);
	//插在左子树上
	else if (pNode->data > value)
	{
		InsertNode(pNode->pLeftChild, value);
		if (GetHeight(pNode->pLeftChild) - GetHeight(pNode->pRightChild) == 2)
		{
			//插在左子树的左子树上,右旋
			if (value < pNode->pLeftChild->data)
				RightRotate(pNode);
			else//插在左子树的右子树上,左旋后右旋 
				LeftRightRotate(pNode);
		}
	}
	//插在右子树上
	else if (pNode->data < value)
	{
		InsertNode(pNode->pRightChild, value);
		if (GetHeight(pNode->pRightChild) - GetHeight(pNode->pLeftChild) == 2)
		{
			//插在右子树的右子树上,左旋
			if (value > pNode->pRightChild->data)
			{
				LeftRotate(pNode);
			}
			//插在右子树的左子树上,先右旋再左旋
			else
			{
				RightLeftRotate(pNode);
			}
		}
	}
	//更新节点高度
	pNode->height = std::max(GetHeight(pNode->pLeftChild), GetHeight(pNode->pRightChild)) + 1;
}

/*
新节点插在右子树的右子树上,导致pNode节点右子树高度比左子树高度大于1
该节点就成为最小失衡子树的根节点(实参pNode)
旋转步骤:
1、pNode右孩子的左子树插入pNode的右子树上
2、pNode作为其右孩子的左子树
3、更新高度
*/
template<typename T>
void AVLTree<T>::LeftRotate(AVLTreeNode<T>* &pNode)
{
	AVLTreeNode<T> *pNodeRightChild = pNode->pRightChild;

	pNode->pRightChild = pNodeRightChild->pLeftChild;
	pNodeRightChild->pLeftChild = pNode;

	pNode->height = std::max(GetHeight(pNode->pLeftChild), GetHeight(pNode->pRightChild)) + 1;
	pNodeRightChild->height = std::max(GetHeight(pNodeRightChild->pLeftChild), GetHeight(pNodeRightChild->pRightChild)) + 1;

	pNode = pNodeRightChild;
}

/*
新节点插在左子树的左子树上,导致pNode节点左子树高度比右子树高度大于1
该节点就成为最小失衡子树的根节点(实参pNode)
旋转步骤:
1、pNode左孩子的右子树作为pNode的左子树上
2、pNode作为其左孩子的右子树
3、更新高度
*/
template<typename T>
void AVLTree<T>::RightRotate(AVLTreeNode<T>* &pNode)
{
	AVLTreeNode<T> *pNodeLeftChild = pNode->pLeftChild;
	pNode->pLeftChild = pNodeLeftChild->pRightChild;
	pNodeLeftChild->pRightChild = pNode;

	pNode->height = std::max(GetHeight(pNode->pLeftChild), GetHeight(pNode->pRightChild)) + 1;
	pNodeLeftChild->height = std::max(GetHeight(pNodeLeftChild->pLeftChild), GetHeight(pNodeLeftChild->pRightChild));

	pNode = pNodeLeftChild;
}

/*
新节点插在左子树的右子树上,导致pNode节点左子树高度比右子树高度大于1
此时需要先左旋再右旋
1、对pNode的左子树作为左旋
2、对pNode右旋
*/
template<typename T>
void AVLTree<T>::LeftRightRotate(AVLTreeNode<T>* &pNode)
{
	LeftRotate(pNode->pLeftChild);
	RightRotate(pNode);
}

/*
新节点插在右子树的左子树上,导致pNode节点左子树高度比右子树高度大于1
此时需要先右旋再左旋
1、对pNode的右子树右旋
2、对pNode左旋
*/
template<typename T>
void AVLTree<T>::RightLeftRotate(AVLTreeNode<T>* &pNode)
{
	RightRotate(pNode->pRightChild);
	LeftRotate(pNode);
}

/*
删除与插入的思路是左右子树相反的,左子树删除相当于在右子树插入,以此类推。
步骤:
1、pNode == null,直接return
2、data==pNode的值时说明找到了数值,则需要删除该节点,删除的步骤:
	(1)判断是否左右子树都存在,存在则用高度高的那一侧的节点pTem(左子树为最大值节点,右子树为最小值节点)
	来代替当前被删除点(因为用高度高的那一侧来替代,此时不会出现失衡);然后递归删除pTem即可
	(2)当左右子树至少有一个不存在时,则用该左、右子树的左、右孩子来替代该点(即删除该节点)
3、data > pNode的值时需要在pNode的右子树上进行删除,此时相当于在左子树上进行插入,会出现(右旋、先左旋后右旋)两种情况
4、data < pNode的值时需要在pNode的左子树上进行删除,此时相当于在右子树上进行插入,会出现(左旋、先右旋后左旋)两种情况
*/
template<typename T>
void AVLTree<T>::Delete(AVLTreeNode<T>*& pNode, T data)
{
	if (pNode == nullptr)
		return;
	if (data == pNode->data)
	{
		if (pNode->pLeftChild != nullptr && pNode->pRightChild != nullptr)
		{
			//左子树高于右子树,则在左子树中找到最大值节点pMaxTmp来替代被删除点,然后删除这个最大值点
			if (GetHeight(pNode->pLeftChild) > GetHeight(pNode->pRightChild))
			{
				AVLTreeNode<T> *pMaxTmp = GetMaxNode(pNode->pLeftChild);//左子树中找到最大节点
				pNode->data = pMaxTmp->data;
				Delete(pNode->pLeftChild, pMaxTmp->data);//递归删除该最大值点pMaxTmp
			}
			//右子树高于左子树,则在右子树中找到最小值节点pMinTmp来替代被删除点,然后删除这个最小值点
			else 
			{
				AVLTreeNode<T> *pMinTmp = GetMinNode(pNode->pRightChild);
				pNode->data = pMinTmp->data;
				Delete(pNode->pRightChild, pMinTmp->data);//递归删除该最小值点pMinTmp
			}
		}
		//左右子树至少有一个不存时,则用其下一个子树来替代该节点,删除该节点
		else
		{
			AVLTreeNode<T> *pDelTmp = pNode;
			pNode = (pDelTmp == nullptr) ? pDelTmp->pRightChild : pDelTmp->pLeftChild;
			delete pDelTmp;
			pDelTmp = nullptr;
		}
	}
	//在右子树上进行删除 
	else if (data > pNode->data)
	{
		Delete(pNode->pRightChild, data);
		if (GetHeight(pNode->pLeftChild) - GetHeight(pNode->pRightChild) == 2)
		{
			//右旋
			if (GetHeight(pNode->pLeftChild->pLeftChild) > GetHeight(pNode->pLeftChild->pRightChild))
			{
				RightRotate(pNode);
			}
			//先左旋再右旋
			else
			{
				LeftRightRotate(pNode);
			}
		}
	}
	//在左子树上进行删除
	else
	{
		Delete(pNode->pLeftChild, data);
		if (GetHeight(pNode->pRightChild) - GetHeight(pNode->pLeftChild) == 2)
		{
			//左旋
			if (GetHeight(pNode->pRightChild->pRightChild) > GetHeight(pNode->pRightChild->pLeftChild))
			{
				LeftRotate(pNode);
			}
			//先右旋再左旋
			else
			{
				RightLeftRotate(pNode);
			}
		}
	}
}


template<typename T>
AVLTreeNode<T>* AVLTree<T>::SearchNode(AVLTreeNode<T>* pNode, T data)
{
#if 1
	//迭代查找
	while (pNode != nullptr)
	{
		if (pNode->data == data)
			return pNode;
		else if (pNode->data > data)
			pNode = pNode->pLeftChild;
		else
			pNode = pNode->pRightChild;
	}
	return nullptr;
#else
	//递归查找
	while (pNode != nullptr)
	{
		if (pNode->data == data)
			return pNode;
		if (pNode->data > data)
			SearchNode(pNode->pLeftChild);
		else
			SearchNode(pNode->pRightChild);
	}
	return nullptr;
#endif
}

template<typename T>
void AVLTree<T>::PreTraver(AVLTreeNode<T>* pNode)
{
	if (pNode == nullptr)
		return;
	std::cout << pNode->data << " ";
	PreTraver(pNode->pLeftChild);
	PreTraver(pNode->pRightChild);
}

//从右子树中找最大的节点
template<typename T>
AVLTreeNode<T>* AVLTree<T>::GetMaxNode(AVLTreeNode<T>* pNode)
{
	if (pNode != nullptr)
	{
		while (pNode->pRightChild != nullptr)
		{
			pNode = pNode->pRightChild;
		}
		return pNode;
	}
	return nullptr;
}

template<typename T>
AVLTreeNode<T>* AVLTree<T>::GetMinNode(AVLTreeNode<T>* pNode)
{
	if (pNode != nullptr)
	{
		while (pNode->pLeftChild != nullptr)
		{
			pNode = pNode->pLeftChild;
		}
		return pNode;
	}
	return nullptr;
}

/*
销毁二叉树
采用后续遍历的方式来销毁
1、先销毁左子树
2、再销毁右子树
3、最后销毁根节点
*/
template<typename T>
void AVLTree<T>::Destroy(AVLTreeNode<T>* pNode)
{
	while (pNode)
	{
		Destroy(pNode->pLeftChild);
		Destroy(pNode->pRightChild);
		//节点
		delete pNode;
		pNode = nullptr;
	}
}
//AVLTree.cpp
#include "AVLTree.h"

int main()
{
	AVLTree<int> tree;
	for (int i = 0; i < 7; i++)
		tree.Insert(i);
	tree.PreTraver();

	tree.Delete(3);
	tree.PreTraver();
	std::cout << std::endl;
	AVLTreeNode<int> *node = tree.Search(1);
	if (node != nullptr)
	{
		std::cout << "find " << node->data << std::endl;
	}
	else
	{
		std::cout << "not find" << std::endl;
	}
		
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值