数据结构树相关第二天-AVL二叉平衡搜索树

前言

在前一篇博客中,谈到二叉搜索树。随机二叉搜索树的平均高度为o(logn),但是会出现极端的情况。如下图所示,则二叉树的高度O(n),在这种已经退化的二叉树下,无论是搜索,还是删除等等操作时间复杂度都为O(n),这我和我们期望的o(logn)有很大差距。我们想,有什么办法让二叉搜索树的高度能保持在o(logn)呢?而这就是我们这片博客AVL平衡二叉树所要讲的主要内容。
《数据结构树相关第二天-AVL二叉平衡搜索树》

AVL树的定义

AVL实际是一个人名的缩写Adelson-Velskii and Landis,为了纪念这伟大的研究成果,从而明明成AVL树。如下图所示,AVL树是指对某一个根节点而言,它的左子树的高度和右子树的高度最大相差1。当然它的左子树和右子树也需要满足这样的性质。有了这个定义,我们可以知道AVL树的高度能保持在O(logn),而这也意味着对于平衡二叉树所有的操作的时间复杂度也都为O(logn),这真是一个很棒的优点啊!
《数据结构树相关第二天-AVL二叉平衡搜索树》

AVL树的插入

在这一篇博客里面,我们主要关注AVL树的插入问题。既然AVL树也是二叉搜索树,那么AVL树的插入前期工作肯定是和二叉搜索树一样的。而AVL树的插入其实重点在于如何使树保持平衡。如下图所示,当我们插入最后的“2”节点时,就会发现这棵树已经不再保持平衡了。在这里,我们需要通过一个很重要的概念“旋转”来纠正这个冲突。
《数据结构树相关第二天-AVL二叉平衡搜索树》

旋转

如下图所示,我们拿一种情况来说明一下,左左情况是指某个节点的左子树的高度比右子树的高度至少大2,很明显这种情况不符合AVL树的定义,而是用旋转我们可以解决这一问题。我们对节点“3”进行右旋,就好像每一个节点都是一个滑轮,我们把“3”这个滑轮拎起来,就可以得到第三行第一列的图,其他的类似。
《数据结构树相关第二天-AVL二叉平衡搜索树》

下面,我将用具体的树的例子来说明每一种情形。

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

如果您有任何疑问和建议,期待您的留言。

    原文作者:爱上健身的菇凉
    原文地址: https://blog.csdn.net/XIAXIA__/article/details/40956781
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞