PTA Root of AVL Tree 思路分析及代码解析

一、前导

1. 需要掌握的知识

  1. 平衡因子:结点的左子树与右子树的高度差
  2. AVL树的特点:平衡因子只能是 1,0,-1 ,因此AVL树的查找效率很高,为log2N。AVL树也称为平衡二叉树。
  3. AVL树自平衡的调整方法
    (1)左左旋转
    (2)右右旋转
    (3)左右旋转
    (4)右左旋转
  4. 通过本题可以深入理解AVL树的特点和自平衡调整方法

2. 题目信息

  1. 题目来源:PTA / 拼题A
  2. 题目地址:https://pintia.cn/problem-sets/16/problems/668

二、解题思路分析

1. 题意理解

  1. 输入数据
7			//AVL树的结点数
88 70 61 96 120 90 65 	//各结点的数值
  1. 输出数据
    打印AVL树的根结点

  2. 题意
    建树并输出根节点信息

2. 思路分析(重点)

  1. 根据录入的树结点信息建立AVL树,由于AVL树的平衡因子的绝对值不能超过1,建树过程会用到AVL树的四种调整方法,本题就是对四种调整方法进行代码具化。

三、具体实现

1. 弯路和bug

  1. 插入树结点 或者 进行旋转后,切记更新树高度
  2. 空树的高度应该是 -1

2. 代码框架(重点)

2.1 采用的数据结构

结构体数组:相较二叉搜索树,增加Height字段用于平衡因子大小的判定

typedef int ElementType;
typedef struct AVLNode *AVLTree;
struct AVLNode
{
	ElementType Data; 
	AVLTree Left;
	AVLTree Right;
	int Height;
};

2.2 程序主体框架

               程序伪码描述
int main()
{	
	1.实现AVL树自平衡的四种调整方法 //core
	2.1的基础上实现 树插入函数
	3.完成建树
}

2.3 各分支函数

  1. SingleLeftRotation() 编码前必须先理解它。
    如下图所示,插入F时,A的平衡因子被破坏了,需要将其左子树B调整为根结点,核心变化就是:B的右子树成为A的左子树,A成为B的右子树。更改后,同步更新A和B的高度。
    由于破坏发生在根结点左子树的左边,因此调整称为左左旋转 或 左单旋。
    在这里插入图片描述
AVLTree SingleLeftRotation(AVLTree A) //左单旋
{
	AVLTree B = A->Left;
	A->Left = B->Right; //B成为根节点后, B的右子树成为A的左子树,A成为B的右子树
	B->Right = A; 
	A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) +1 ;
	B->Height = Max(GetHeight(B->Left),A->Height) + 1;
	return B; 
}
  1. SingleRightRotation( ) 右单旋
    如下图所示(手残图丑 囧),插入C时,A的平衡因子被破坏,需要将其右子树B调整为根结点,核心变化就是:B的左子树(NULL)成为A的右子树,A成为B的左子树。更改后,同步更新A和B的高度。
    A左子树非空的情况,大家可以自己试一下,旋转变化都一样。
    由于破坏发生在根结点右子树的右边,因此调整称为右右旋转 或 右单旋。
    接下来的左右旋转和右左旋转会用到前面已实现的左单旋和右单旋函数
    在这里插入图片描述
AVLTree SingleRightRotation(AVLTree A) //右单旋
{  
	AVLTree B=A->Right;
	A->Right=B->Left; //B成为根节点后, B的左子树成为A的右子树,A成为B的左子树
	B->Left=A; 
	A->Height = Max(GetHeight(A->Left),GetHeight(A->Right)) +1;
	B->Height = Max( A->Height , GetHeight(B->Right)) + 1;
	return B;
} 
  1. DoubleLeftRightRotation( ) 左右旋转
    3.1 代码虽然很简练,但是理解起来有一定难度。
    3.2 如下图所示,插入C时,A的平衡因子被破坏,需要将插入的C调整为根结点。 将第二大的结点调整为根节点。
    3.3 因此需要进行两次变化:(1)先调整A的左子树:将C调整为左子树的根结点。此次调整就是右单旋,C的左子树成为B的右子树,B成为C的左子树。(2)然后对树A进行左单旋,将C调整为根结点。
    3.4 A右子树非空的情况,请自己试一下,可以加深理解,旋转变化和A右子树为空时是一样的。
    3.5 由于破坏发生在根结点左子树的右边,因此称为左右旋转。
    右左旋转同理
    在这里插入图片描述
AVLTree DoubleLeftRightRotation(AVLTree A)
{
	A->Left=SingleRightRotation(A->Left); 
	return  SingleLeftRotation(A); 
}
  1. DoubleRightLeftRotation( ) 右左旋转
    先进行右子树的左单旋,再进行整个树的右单旋,从而将插入结点调整为根结点。请参考 3.DoubleLeftRightRotation( ),最好自己画一画
AVLTree DoubleRightLeftRotation(AVLTree A)
{
	A->Right= SingleLeftRotation(A->Right) ;
	return SingleRightRotation(A);
}
  1. Insert() 根据1-4完成AVL树结点的插入函数
    5.1 分为三种情况:树空,新增左子树,新增右子树
    5.2 新增左子树 或 右子树 均可能导致AVL的平衡因子被破坏,当满足破坏条件时(平衡因子超过1)进行旋转调整
    5.3 调整时请注意区分四种旋转形态
AVLTree Insert(AVLTree T,ElementType X)
{
	if(!T) //树空 
	{
		T=(AVLTree)malloc(sizeof(struct AVLNode));
		T->Data=X;
		T->Height=0;
		T->Left=T->Right=NULL;
	}
	else if(X<T->Data) //新增左子树 
	{
		T->Left=Insert(T->Left,X);
		if(GetHeight(T->Left)-GetHeight(T->Right)==2)
		{
			if(X<T->Left->Data)
				T=SingleLeftRotation(T);
			else
				T=DoubleLeftRightRotation(T);
		}
	}
	else if(X>T->Data) //新增右子树 
	{
		T->Right = Insert(T->Right,X);
		if(GetHeight(T->Right) - GetHeight(T->Left) == 2 )
		{
			if(X > T->Right->Data)
				T=SingleRightRotation(T); //不要忘记赋值 
			else if(X<T->Right->Data)
				T=DoubleRightLeftRotation(T);
		}
	}
	
	T->Height=Max(GetHeight(T->Left),GetHeight(T->Right)) + 1;
	
	return T;
}
  1. GetHeight() 获取树的高度
    注意:在代码中,空树的高度设定为-1,这样计算树高度时才符合实际情况
    max( ) 获取较大值,属于基本功,这里就不单独列出了
int GetHeight(AVLTree Tree)
{
	if(!Tree) 
		return -1;
	else
		return Tree->Height;
}

3. 完整编码

如果本文帮到了您,请点赞鼓励,您的鼓励是作者持续原创的动力,谢谢 😃

#include<queue>
#include<cstdlib>
#include<iostream>
using namespace std;

typedef int ElementType;
typedef struct AVLNode *AVLTree;
struct AVLNode
{
	ElementType Data; 
	AVLTree Left;
	AVLTree Right;
	int Height;
};

int Max(int a,int b);
int GetHeight(AVLTree Tree);

AVLTree DoubleRightLeftRotation(AVLTree A);
AVLTree DoubleLeftRightRotation(AVLTree A);
AVLTree SingleRightRotation(AVLTree A);
AVLTree SingleLeftRotation(AVLTree A) ;

AVLTree Insert(AVLTree T,ElementType X);

int main()
{
	int NodesNumber; ElementType x;
	cin>>NodesNumber;
	
	AVLTree AVL=NULL; 
	for(int i=0;i<NodesNumber;i++)
	{
		cin>>x;
		AVL=Insert(AVL,x);
	}
	cout<<AVL->Data;
	return 0;
}

AVLTree Insert(AVLTree T,ElementType X)
{
	if(!T) //树空 
	{
		T=(AVLTree)malloc(sizeof(struct AVLNode));
		T->Data=X;
		T->Height=0;
		T->Left=T->Right=NULL;
	}
	else if(X<T->Data) //新增左子树 
	{
		T->Left=Insert(T->Left,X);
		if(GetHeight(T->Left)-GetHeight(T->Right)==2)
		{
			if(X<T->Left->Data)
				T=SingleLeftRotation(T);
			else
				T=DoubleLeftRightRotation(T);
		}
	}
	else if(X>T->Data) //新增右子树 
	{
		T->Right = Insert(T->Right,X);
		if(GetHeight(T->Right) - GetHeight(T->Left) == 2 )
		{
			if(X > T->Right->Data)
				T=SingleRightRotation(T); //不要忘记赋值 
			else if(X<T->Right->Data)
				T=DoubleRightLeftRotation(T);
		}
	}
	
	T->Height=Max(GetHeight(T->Left),GetHeight(T->Right)) + 1;
	
	return T;
}


AVLTree DoubleRightLeftRotation(AVLTree A)
{
	A->Right= SingleLeftRotation(A->Right) ;
	return SingleRightRotation(A);
}

AVLTree DoubleLeftRightRotation(AVLTree A)
{
	A->Left=SingleRightRotation(A->Left); 
	return  SingleLeftRotation(A); 
}

AVLTree SingleRightRotation(AVLTree A) //右单旋
{  
	AVLTree B=A->Right;
	A->Right=B->Left; //B成为根节点后, B的左子树成为A的右子树,A成为B的左子树
	B->Left=A; 
	A->Height = Max(GetHeight(A->Left),GetHeight(A->Right)) +1;
	B->Height = Max( A->Height , GetHeight(B->Right)) + 1;
	return B;
} 

AVLTree SingleLeftRotation(AVLTree A) //左单旋
{
	AVLTree B = A->Left;
	A->Left = B->Right; //B成为根节点后, B的右子树成为A的左子树,A成为B的右子树
	B->Right = A; 
	A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) +1 ;
	B->Height = Max(GetHeight(B->Left),A->Height) + 1;
	return B; 
}

int GetHeight(AVLTree Tree)
{
	if(!Tree) 
		return -1;
	else
		return Tree->Height;
}


int Max(int a,int b)
{
	return a>b?a:b;
}



2021.10.9 AC代码:全靠过去的自己续命

#include <iostream>
using namespace std;

typedef struct Node* AVL;
typedef int ElementType;

struct Node
{
	ElementType Value;
	AVL Left;
	AVL Right;
	int Height;
};

AVL Insert(AVL Tree, ElementType Value);

AVL SigalLeftRotate(AVL Tree);
AVL SigalRightRotate(AVL Tree);
AVL LeftRightRotate(AVL Tree);
AVL RightLeftRotate(AVL Tree);

int GetTreeHeight(AVL Tree);
int Max(int a, int b);
void FreeMem(AVL Tree);

int main()
{
	int N; cin >> N;

	AVL Tree = NULL; ElementType Value;

	for (int i = 0; i < N; i++)
	{
		cin >> Value;
		Tree = Insert(Tree, Value);
	}
	cout << Tree->Value;
	FreeMem(Tree);

	return 0;
}

AVL SigalLeftRotate(AVL Tree) //左单旋:树的左子树的右子树 成为 树的左子树,树 成为左子树的 右子树
{
	AVL Left = Tree->Left;

	Tree->Left = Left->Right;
	Left->Right = Tree;

	Tree->Height = Max(GetTreeHeight(Tree->Left), GetTreeHeight(Tree->Right)) + 1;// +1是因为 空树高度为-1、单点树高度为0
	Left->Height = Max(GetTreeHeight(Left->Left), Tree->Height) + 1;

	return Left;
}
AVL SigalRightRotate(AVL Tree)//右单旋:树的右子树的左子树 成为 树的右子树,树 成为右子树的 左子树
{
	AVL Right = Tree->Right;
	Tree->Right = Right->Left;
	Right->Left = Tree;
	Tree->Height = Max(GetTreeHeight(Tree->Left), GetTreeHeight(Tree->Right)) + 1;
	Right->Height = Max(Tree->Height, GetTreeHeight(Right->Right));

	return Right;
}
AVL LeftRightRotate(AVL Tree) //左右旋:先调整树的左子树,进行右单旋;接下来对树进行左单旋
{
	Tree->Left=SigalRightRotate(Tree->Left);
	return SigalLeftRotate(Tree);
}
AVL RightLeftRotate(AVL Tree) //右左旋:先调整树的右子树,进行左单旋;接下来对树进行右单旋
{
	Tree->Right = SigalLeftRotate(Tree->Right);
	return SigalRightRotate(Tree);
}

AVL Insert(AVL Tree, ElementType Value)
{
	if (!Tree)
	{
		Tree = new struct Node;
		Tree->Value = Value; 
		Tree->Left = Tree->Right = NULL;
		Tree->Height = 0; //单节点树高度为0
	}
	else
	{
		if (Value > Tree->Value)
		{
			Tree->Right = Insert(Tree->Right, Value);
			if (GetTreeHeight(Tree->Right) - GetTreeHeight(Tree->Left) >= 2)
			{
				if (Value > Tree->Right->Value)
					Tree = SigalRightRotate(Tree);
				else
					Tree = RightLeftRotate(Tree);
			}
		}
		else if (Value < Tree->Value)
		{
			Tree->Left = Insert(Tree->Left, Value);
			if (GetTreeHeight(Tree->Left) - GetTreeHeight(Tree->Right) >= 2)
			{
				if (Value < Tree->Left->Value)
					Tree = SigalLeftRotate(Tree);
				else
					Tree = LeftRightRotate(Tree);
			}
		}
	}

	Tree->Height = Max(GetTreeHeight(Tree->Left), GetTreeHeight(Tree->Right)) + 1;

	return Tree;
}

void FreeMem(AVL Tree)
{
	if (!Tree)	return;
	FreeMem(Tree->Left);
	FreeMem(Tree->Right);
	delete Tree;
	return;
}

int GetTreeHeight(AVL Tree)
{
	if (Tree == NULL) return -1; //空树高度为-1
	else return Tree->Height;
}

int Max(int a, int b)
{
	if (a >= b) return a;
	else return b;
}

四、参考

浙江大学 陈越、何钦铭老师主讲的数据结构

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值