C++实现AVL树(四种旋转详解)
2018-01-23 13:02
921 查看
一、概念
AVL树又称为高度平衡的二叉搜索树,它能保持二叉树的高度平衡,尽量降低二叉树的高度,减少树的平均搜索长度。二、性质
树中每个左子树和右子树都是AVL树左子树和右子树高度之差的绝对值(简称平衡因子)不超过1(0,1,-1)
平衡因子就是用右子树的高度减去左子树的高度
红色的就是每个节点的平衡因子
三、AVL树的插入
如果在一棵原本是平衡的二叉搜索树中插入一个新节点,可能造成不平衡,此时必须调整树 的结构,使之平衡化。插入算法:
如果是空树,插入后即为根节点,插入后直接返回true如果树不空,寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回false
插入结点
更新平衡因子,对树进行调整
新节点pcur平衡因子为0,但其双亲结点parent的平衡因子有三种情况:
如果parent的平衡因子为0即在parent较矮的子树上插入新节点,parent平衡,其高度没有增加,此时从parent到 根路径 上各结点为根的子树的高度不变,即各结点的平衡因子不变,结束平衡化处理。
如果parent的平衡因子的绝对值为1
插入前parent的平衡因子为0,插入后以parent为根的子树没有失去平衡,但该子树 的高度增 加,需从parent向根节点方向回溯,继续查看parent的双亲的平衡性。
在上述更新后,如果parent平衡因子的绝对值为2,新节点在较高的子树插入,需要做平衡化处理:
若parent->_bf == 2,说明右子树高,设parent的右子subR
当subR的平衡因子为1,执行左单旋转
当subR的平衡因子为-1,执行先右后左双旋转
若parent->_bf == -2,说明左子树高,设parent的左子树subL
当subL的平衡因子为-1,执行右单旋转
当subL的平衡因子为1,执行先左后右双旋转
旋转后parent为根的子树高度降低,无需继续向上层回溯
1.左单旋
插入节点位于较高右子树的右侧如下图所示:
//左单旋 void RotateL(pNode parent) { pNode subR = parent->_pRightChild; pNode subRL = subR->_pLeftChild; parent->_pRightChild = subRL; if (subRL) subRL->_pParent = parent; subR->_pLeftChild = parent; pNode pparent = parent->_pParent; subR->_pParent = pparent; parent->_pParent = subR; if (parent == _pRoot) { _pRoot = subR; } else//更新祖父节点的左右孩子 { if (pparent->_pLeftChild == parent) pparent->_pLeftChild = subR; else pparent->_pRightChild = subR; } //更新平衡因子 parent->_bf = subR->_bf = 0; }
2.右单旋
插入节点位于较高左子树的左侧如下图所示:
void RotateR(pNode parent) { pNode subL = parent->_pLeftChild; pNode subLR = subL->_pRightChild; parent->_pLeftChild = subLR; if (subLR) subLR->_pParent = parent; subL->_pRightChild = parent; pNode pparent = parent->_pParent; parent->_pParent = subL; subL->_pParent = pparent; if (parent == _pRoot) { _pRoot = subL; } else { if (pparent->_pLeftChild == parent) pparent->_pLeftChild = subL; else pparent->_pRightChild = subL; } //更新平衡因子 parent->_bf = subL->_bf = 0; }
3.左右双旋
插入节点在较高左子树的右侧//左右双旋 void RotateLR(pNode parent) { pNode pSubL = parent->_pLeftChild; pNode pSubLR = NULL; if (pSubL) pSubLR = pSubL->_pRightChild; RotateL(parent->_pLeftChild);//先对parent的左孩子进行左旋 RotateR(parent);//对parent进行右旋 if (pSubLR->_bf == 1) parent->_bf = 0; else if (pSubLR->_bf == -1) parent->_bf = 1; }
4.右左双旋
插入节点位于较高右子树的左侧//右左双旋 void RotateRL(pNode parent) { pNode pSubR = parent->_pRightChild; pNode pSubRL = NULL; if (pSubR!=NULL) pSubRL = pSubR->_pLeftChild; RotateR(parent->_pRightChild); RotateL(parent); if (pSubRL->_bf == 1) parent->_bf = -1; else if (pSubRL->_bf == -1) parent->_bf = 0; }
4.AVL的删除
(在这里简单介绍一下删除的算法,不做具体的代码实现)从AVL树中删除一个节点,首先必须检测该结点是否存在,若存在删除该结点之后可能会破 坏AVL树的高度平衡,因此需要做平衡化旋转。
被删除的结点pcur存在以下情况:
被删除结点只有左孩子
被删除结点只有右孩子
被删除结点左右孩子都存在–>变为删除中序遍历下的第一个结点q
更新parent的平衡因子,若q是parent的左孩子,则parent的平衡因子增加1,否则减少1,根据修改后的parent的平衡因子调整到根路径上的所有节点:
parent平衡因子的平衡因子为1或-1,parent高度不变,则从parent到根所有节点高度均 不变,不用调整。
parent的平衡因子变成0,虽然以parent为根的子树平衡,其高度减1,但需要检查 parent的双亲结点的平衡性。
结点parent的平衡因子为2或-2,则较矮子树被缩短,parent发生不平衡,需要进行平衡化旋转。
令parent较高子树的根为q,根据q的平衡因子,分一下三种情况
如果q的平衡因子为0,执行单旋转恢复parent
如果q的平衡因子与parent平衡因子(正负)号相同,则执行一个单旋转恢复 parent
如果q的平衡因子与parent平衡因子(正负)号相反,则执行一个双旋转恢复 parent
完整代码
#pragma once
#include <iostream>
using namespace std;
#include <stdlib.h>
template <class K,class V>
struct AVLTreeNode
{
AVLTreeNode(const K& key,const V& value)
:_pLeftChild(NULL)
, _pRightChild(NULL)
, _pParent(NULL)
, _key(key)
, _value(value)
, _bf(0)
{}
AVLTreeNode<K, V>* _pLeftChild;
AVLTreeNode<K, V>* _pRightChild;
AVLTreeNode<K, V>* _pParent;
K _key;
V _value;
int _bf;
};
template <class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
typedef AVLTreeNode<K, V>* pNode;
public:
AVLTree()
:_pRoot(NULL)
{}
//插入
bool Insert(const K& key, const V& value)
{
//空树的插入
if (_pRoot == NULL)
{
_pRoot = new Node(key, value);
return true;
}
pNode parent = NULL;
pNode cur = _pRoot;
//寻找待插入节点的位置
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_pLeftChild;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_pRightChild;
}
else
return false;
}
//插入节点
cur = new Node(key, value);
if (parent->_key < key)
{
parent->_pRightChild = cur;
cur->_pParent = parent;
}
else
{
parent->_pLeftChild = cur;
cur->_pParent = parent;
}
//调整树
while (parent)
{
if (parent->_pRightChild == cur)
parent->_bf++;
else
parent->_bf--;
if (parent->_bf == 0)
break;
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = cur->_pParent;
}
else
{
if (parent->_bf == 2)
{
if (cur->_bf == 1)//插入节点位于右孩子处
RotateL(parent);
else
RotateRL(parent);
}
else
{
if (cur->_bf == -1)
RotateR(parent);
else
RotateLR(parent);
}
break;
}
}
return true;
}
//中序遍历
void InOrder()
{
_InOrder(_pRoot);
cout << endl;
}
//判断是否为平衡二叉树
bool IsBalance()
{
return _IsBalance(_pRoot);
}
//求树的高度
int Height()
{
return _Height(_pRoot);
}
private:
void _InOrder(pNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeftChild);
cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
_InOrder(pRoot->_pRightChild);
}
}
int _Height(pNode pRoot)
{
if (pRoot == NULL)
return 0;
int leftHeight = _Height(pRoot->_pLeftChild);
int rightHeight = _Height(pRoot->_pRightChild);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalance(pNode pRoot)
{
if (pRoot == NULL)
return true;
int leftHeight = _Height(pRoot->_pLeftChild);
int rightHeight = _Height(pRoot->_pRightChild);
if (abs(leftHeight - rightHeight) >= 2)
return false;
return _IsBalance(pRoot->_pLeftChild) && _IsBalance(pRoot->_pRightChild);
}
//左单旋
void RotateL(pNode parent)
{
pNode subR = parent->_pRightChild;
pNode subRL = subR->_pLeftChild;
parent->_pRightChild = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeftChild = parent;
pNode pparent = parent->_pParent;
subR->_pParent = pparent;
parent->_pParent = subR;
if (parent == _pRoot)
{
_pRoot = subR;
// subR->_pParent = NULL;
}
else//更新祖父节点的左右孩子
{
if (pparent->_pLeftChild == parent)
pparent->_pLeftChild = subR;
else
pparent->_pRightChild = subR;
}
parent->_bf = subR->_bf = 0;
}
//右单旋
void RotateR(pNode parent)
{
pNode subL = parent->_pLeftChild;
pNode subLR = subL->_pRightChild;
parent->_pLeftChild = subLR;
if (subLR)
subLR->_pParent = parent;
subL->_pRightChild = parent;
pNode pparent = parent->_pParent;
parent->_pParent = subL;
subL->_pParent = pparent;
if (parent == _pRoot)
{
_pRoot = subL;
// subL->_pParent = NULL;
}
else
{
if (pparent->_pLeftChild == parent)
pparent->_pLeftChild = subL;
else
pparent->_pRightChild = subL;
}
parent->_bf = subL->_bf = 0;
}
//右左双旋 void RotateRL(pNode parent) { pNode pSubR = parent->_pRightChild; pNode pSubRL = NULL; if (pSubR!=NULL) pSubRL = pSubR->_pLeftChild; RotateR(parent->_pRightChild); RotateL(parent); if (pSubRL->_bf == 1) parent->_bf = -1; else if (pSubRL->_bf == -1) parent->_bf = 0; }
//左右双旋
void RotateLR(pNode parent)
{
pNode pSubL = parent->_pLeftChild;
pNode pSubLR = NULL;
if (pSubL)
pSubLR = pSubL->_pRightChild;
RotateL(parent->_pLeftChild);
RotateR(parent);
if (pSubLR->_bf == 1)
parent->_bf = 0;
else if (pSubLR->_bf == -1)
parent->_bf = 1;
}
private:
pNode _pRoot;
};
测试代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include "AVLTree.h" void Test() { int arr[10] = { 2, 5, 0, 6, 9, 1, 4, 3, 7, 8 }; AVLTree<int, int> avl; for (int i = 0; i < 10; i++) { avl.Insert(arr[i], i); } avl.InOrder(); cout << "avl is balance?" << avl.IsBalance() << endl; cout << "avl树高度为:" << avl.Height() << endl; } int main() { Test(); system("pause"); return 0; }
运行结果:
相关文章推荐
- C++模板实现二叉树(六 AVL树基础与旋转)
- 数据结构图文解析之:AVL树详解及C++模板实现
- C++模板实现二叉树(六 AVL树基础与旋转)
- C++模板实现二叉树(六 AVL树基础与旋转)
- C++模板实现二叉树(六 AVL树基础与旋转)
- 数据结构图文解析之:AVL树详解及C++模板实现
- 平衡二叉搜索树(AVL树)的原理及实现源代码(有图文详解和C++、Java实现代码)
- C++实现四种三角形的打印
- 四种古典密码的C++实现(3)-----Playfair密码
- 最近邻图像旋转代码 灰度图像 c++实现
- C++模板的声明与实现分离 编译错误详解
- BMP图像旋转----C++实现
- BMP图像旋转----C++实现
- 二叉查找树详解及C++实现
- 剑指offer刷题之c++实现的旋转数组的最小数字
- 一步一步详解ID3和C4.5的C++实现
- jQuery+CSS3实现四种应用广泛的导航条制作实例详解
- hdu 1233 还是畅通工程(最小生成树的Prim和Kruskal两种算法的c++实现)(prim算法详解)
- 快速图像旋转算法的c++实现
- 数据结构图文解析之:二叉堆详解及C++模板实现