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;
}