前言
在前一篇博客中,谈到二叉搜索树。随机二叉搜索树的平均高度为o(logn),但是会出现极端的情况。如下图所示,则二叉树的高度O(n),在这种已经退化的二叉树下,无论是搜索,还是删除等等操作时间复杂度都为O(n),这我和我们期望的o(logn)有很大差距。我们想,有什么办法让二叉搜索树的高度能保持在o(logn)呢?而这就是我们这片博客AVL平衡二叉树所要讲的主要内容。
AVL树的定义
AVL实际是一个人名的缩写Adelson-Velskii and Landis,为了纪念这伟大的研究成果,从而明明成AVL树。如下图所示,AVL树是指对某一个根节点而言,它的左子树的高度和右子树的高度最大相差1。当然它的左子树和右子树也需要满足这样的性质。有了这个定义,我们可以知道AVL树的高度能保持在O(logn),而这也意味着对于平衡二叉树所有的操作的时间复杂度也都为O(logn),这真是一个很棒的优点啊!
AVL树的插入
在这一篇博客里面,我们主要关注AVL树的插入问题。既然AVL树也是二叉搜索树,那么AVL树的插入前期工作肯定是和二叉搜索树一样的。而AVL树的插入其实重点在于如何使树保持平衡。如下图所示,当我们插入最后的“2”节点时,就会发现这棵树已经不再保持平衡了。在这里,我们需要通过一个很重要的概念“旋转”来纠正这个冲突。
旋转
如下图所示,我们拿一种情况来说明一下,左左情况是指某个节点的左子树的高度比右子树的高度至少大2,很明显这种情况不符合AVL树的定义,而是用旋转我们可以解决这一问题。我们对节点“3”进行右旋,就好像每一个节点都是一个滑轮,我们把“3”这个滑轮拎起来,就可以得到第三行第一列的图,其他的类似。
下面,我将用具体的树的例子来说明每一种情形。
a) Left Left Case
T1, T2, T3 and T4 are subtrees. z y / \ / \ y T4 Right Rotate (z) x z / \ - - - - - - - - -> / \ / \ x T3 T1 T2 T3 T4 / \ T1 T2
b) Left Right Case
z z x / \ / \ / \ y T4 Left Rotate (y) x T4 Right Rotate(z) y z / \ - - - - - - - - -> / \ - - - - - - - -> / \ / \ T1 x y T3 T1 T2 T3 T4 / \ / \ T2 T3 T1 T2
c) Right Right Case
z y / \ / \ T1 y Left Rotate(z) z x / \ - - - - - - - -> / \ / \ T2 x T1 T2 T3 T4 / \ T3 T4
d) Right Left Case
z z x / \ / \ / \ T1 y Right Rotate (y) T1 x Left Rotate(z) z y / \ - - - - - - - - -> / \ - - - - - - - -> / \ / \ x T4 T2 y T1 T2 T3 T4 / \ / \ T2 T3 T3 T4可以看到不管怎么失衡,用旋转都能解决问题。
完整代码
#include "stdafx.h"
#include <string>
#include <stdlib.h>
#include <iostream>
using namespace std;
typedef struct Node
{
int key; //键值
struct Node *left; //左节点
struct Node *right; //右节点
struct Node *father; //父节点
int height; //节点出现的次数
} Node, *pNode;
void creatAVLTree(pNode &pAVLTree, int *ptr, int len);
pNode insertNode(pNode &pAVLTree, int value);
int getBlanceFactor(pNode &pRoot);
int getHeight(pNode &pRoot);
void mallocInitNode(pNode &pInsertNode, int value);
pNode rightRotate(pNode &pRoot);
pNode leftRotate(pNode &pRoot);
void preordertraversal(pNode &pRoot);
int main()
{
int a[] = {10, 20, 30, 40, 50, 25};
int len = sizeof(a) / sizeof(int);
pNode pAVLTree = NULL;
/* 创建AVL树 */
creatAVLTree(pAVLTree, a, len);
/* 得到AVL树的中序遍历 */
preordertraversal(pAVLTree);
return 0;
}
/* 获得AVL树的前序遍历 */
void preordertraversal(pNode &pRoot)
{
if (NULL == pRoot)
{
return;
}
cout << pRoot->key << " ";
preordertraversal(pRoot->left);
preordertraversal(pRoot->right);
}
/* 创建一个二叉平衡查找树 */
void creatAVLTree(pNode &pAVLTree, int *ptr, int len)
{
pNode root = NULL;
for (int i = 0; i < len; i++)
{
root = insertNode(root, *(ptr + i));
}
pAVLTree = root;
}
/* 插入一个节点,复杂度为O(logn) */
pNode insertNode(pNode &pAVLTree, int value)
{
pNode pInsertNode;
/* 1.第一个节点,直接插入 */
if (NULL == pAVLTree)
{
mallocInitNode(pInsertNode, value);
pAVLTree = pInsertNode;
return pAVLTree;
}
/* 2.小于,递归左子树,插入 */
if (value < pAVLTree->key)
{
insertNode(pAVLTree->left, value);
}
else
{
insertNode(pAVLTree->right, value);
}
/* 3.该节点被插入后,需要更新树的高度 */
pAVLTree->height = max(getHeight(pAVLTree->left), getHeight(pAVLTree->right)) + 1;
/* 4.该节点被插入后,需要获取该节点左子树和右子树的高度差 */
int blanceFactor = getBlanceFactor(pAVLTree);
/* 5.根据高度差,旋转调整 */
if ((2 <= blanceFactor) && (value < pAVLTree->left->key))
{
//case 1,左左,对根节点进行右旋转
pAVLTree = rightRotate(pAVLTree);
return pAVLTree;
}
if ((-2 >= blanceFactor) && (value > pAVLTree->right->key))
{
//case 2, 右右,对根节点进行左旋转
pAVLTree = leftRotate(pAVLTree);
return pAVLTree;
}
if ((2 <= blanceFactor) && (value > pAVLTree->left->key))
{
//case 3,左右,先对Y节点进行左旋转,而后对Z节点进行右旋转
pAVLTree->left = leftRotate(pAVLTree->left);
return rightRotate(pAVLTree);
}
if ((-2 >= blanceFactor) && (value < pAVLTree->right->key))
{
//case 4, 右左,先对Y节点进行右旋转,而后对Z节点进行左旋转
pAVLTree->right = rightRotate(pAVLTree->right);
return leftRotate(pAVLTree);
}
return pAVLTree;
}
pNode leftRotate(pNode &pRoot)
{
pNode pYRightChild = pRoot->right->left; //将Y节点的左孩子保存下来
pNode pYNode = pRoot->right; //将Y节点也保存下来
/* 1. Z节点变为Y的左孩子 */
pYNode->left = pRoot;
/* 2.Y节点的左孩子,变成Z节点的右孩子 */
pRoot->right = pYRightChild;
/* 3.更新Y节点和Z节点的高度 */
pRoot->height = max(getHeight(pRoot->right), getHeight(pRoot->left)) + 1;
pYNode->height = max(getHeight(pYNode->right), getHeight(pYNode->left)) + 1;
/* 4.返回新的根节点 */
return pYNode;
}
pNode rightRotate(pNode &pRoot)
{
pNode pYRightChild = pRoot->left->right; //将Y节点的右孩子保存下来
pNode pYNode = pRoot->left; //将Y节点也保存下来
/* 1. Z节点变为Y的右孩子 */
pYNode->right = pRoot;
/* 2.Y节点的右孩子,变成Z节点的左孩子 */
pRoot->left = pYRightChild;
/* 3.更新Y节点和Z节点的高度 */
pRoot->height = max(getHeight(pRoot->left), getHeight(pRoot->right)) + 1;
pYNode->height = max(getHeight(pYNode->left), getHeight(pYNode->right)) + 1;
/* 4.返回新的根节点 */
return pYNode;
}
int getBlanceFactor(pNode &pRoot)
{
int leftHeight = getHeight(pRoot->left);
int rightHeight = getHeight(pRoot->right);
return leftHeight - rightHeight;
}
int getHeight(pNode &pRoot)
{
/* 如果该节点为空节点的话 */
if (NULL == pRoot)
{
return -1;
}
/* 如果该节点不为空的话 */
return pRoot->height;
}
/* 创建新节点并初始化 */
void mallocInitNode(pNode &pInsertNode, int value)
{
pInsertNode = (pNode)malloc(sizeof(Node));
pInsertNode->key = value;
pInsertNode->left = NULL;
pInsertNode->right = NULL;
pInsertNode->height = 0;
}
如果您有任何疑问和建议,期待您的留言。