阅读 74

平衡二叉树(AVL)原理解析与实现(C++)

1. 简介

1.1 定义

平衡二叉查找树:简称平衡二叉树。在计算机科学中,AVL树是最早被发明的自平衡二叉查找树。在AVL树中,任一节点对应的两棵子树的最大高度差为1,因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是O(logn)。增加和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡。AVL 树得名于它的发明者 G. M. Adelson-Velsky 和 Evgenii Landis,他们在1962年的论文《An algorithm for the organization of information》中公开了这一数据结构。

1.2 性质

一棵AVL树有如下必要条件:

  • 它必须是二叉查找树。
  • 每个节点的左子树和右子树的高度差至多为1。

AVL树的查找、插入、删除操作在平均和最坏的情况下都是O(logn),这得益于它时刻维护着二叉树的平衡。如果我们需要查找的集合本身没有顺序,在频繁查找的同时也经常的插入和删除,AVL树是不错的选择。不平衡的二叉查找树在查找时的效率是很低的,因此,AVL如何维护二叉树的平衡是我们的学习重点。

2. AVL的实现

2.1 结点结构

struct AVLNode {
	int data;
	AVLNode *lChild, *rChild;
	AVLNode(int a) {
		data = a;
		lChild = NULL;
		rChild = NULL;
	}
};
复制代码

2.2 平衡二叉树的基本框架

class AVLTree {
public:
	AVLTree();
	~AVLTree();

	///从命令行接受数据创建AVL树。使用其他方式创建原理一致
	void Create();

	///输出该排序树
	void Print();

	///先序遍历输出
	void PreOrder(AVLNode *p);
	///中序遍历输出。
	void MidOrder(AVLNode *p);

	///返回二叉树高度
	int Height(AVLNode *p);

	///返回根节点
	AVLNode* getRoot() {
		return root;
	}

	///简化版插入函数
	void Insert(const int x);

	///简化版删除函数
	void Remove(int x);

	///返回值为x的结点指针
	AVLNode* Search(int x);

	///返回子树最大值结点的指针
	AVLNode* TreeMax;

	///返回子树最小值结点的指针
	AVLNode* TreeMin(AVLNode* subTree);

private:
	///根节点
	AVLNode *root;

	///插入结点(因为对任一结点进行插入操作后都需要平衡操作,可能会改变该处的结点,
	///因此设置返回值记录完成操作后此处的结点指针)
	AVLNode* Insert(AVLNode* subRoot, const int k);

	///删除结点(视为在该结点为根节点的树上进行删除操作)
	AVLNode* Remove(AVLNode* subRoot, int x);

	///返回某个结点的平衡因子
	int BalanceFactor(AVLNode *p);

	///对某个结点进行平衡操作(根据平衡因子调用四种不同的旋转操作)
	AVLNode* Balancee(AVLNode* subRoot);
	/*****************************
	          四种旋转操作
	******************************/
	/// LL平衡旋转(右单旋转):在左孩子(L)的左子树(L)上插入导致不平衡
	AVLNode* LL_Rotation(AVLNode *subRoot);

	/// RR平衡旋转(左单旋转):在右孩子(R)的右子树(R)上插入导致不平衡
	AVLNode* RR_Rotation(AVLNode *subRoot);

	/****下面两种情况可看作是对根节点和子节点进行上两种旋转操作的组合******/
	/// RL平衡旋转(先右后左双旋转):在右孩子(R)的左子树(L)上插入导致不平衡
	AVLNode* RL_Rotation(AVLNode *subRoot);

	/// LR平衡旋转(先左后右双旋转): 在左孩子(L)的右子树(R)上插入导致不平衡
	AVLNode* LR_Rotation(AVLNode *subRoot);

	///销毁该树
	void destroy(AVLNode* p);
};
复制代码

2.3 重要成员函数原理解析

2.3.1 四种旋转操作函数

  在理解AVL旋转之前,首先得知道以下几个概念:
  1. AVL 树节点的插入总是在叶子节点。
  2. AVL 树在插入节点之前总是满足平衡条件的。
  3. 插入新节点后有可能满足平衡条件也有可能不满足。
  4. 当不满足平衡条件后,我们就需要对新的树进行旋转。

1) LL平衡旋转(右单旋转)

  由于在A的左孩子(L)的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1增至2。
  LL型调整的一般形式如下图1所示,表示在A的左孩子B的左子树BL(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:
  ①将A的左孩子B提升为新的根结点 ;
  ②将原来的根结点A降为B的右孩子;
  ③各子树按大小关系连接(BL和AR不变,BR调整为A的左子树)。

             图1 一般形式的LL型调整
             图2 LL旋转实例

	/// LL平衡旋转(右单旋转)
	//在左孩子(L)的左子树(L)上插入导致不平衡,需要向右旋转一次实现平衡
	AVLNode* LL_Rotation(AVLNode *subRoot) {
		AVLNode* temp = subRoot->lChild;
		subRoot->lChild = temp->rChild;
		temp->rChild = subRoot;
		//完成旋转操作之后,该处分支结点(原为subRoot)发生了变化,
		//因此要返回新的分支节点指针供其父节点更新孩子指针
		return temp;
	}
复制代码

2) RR平衡旋转(左单旋转)

  由于在A的右孩子(R)的右子树(R)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。
  RR型调整的一般形式如下图3所示,表示在A的右孩子B的右子树BR(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡(h 表示子树的深度)。这种情况调整如下:
  ①将A的右孩子B提升为新的根结点;
  ②将原来的根结点A降为B的左孩子;
  ③各子树按大小关系连接(AL和BR不变,BL调整为A的右子树)。

            图3 一般形式的RR型调整
            图4 RR旋转实例

	/// RR平衡旋转(左单旋转)
	//在右孩子(R)的右子树(R)上插入导致不平衡,需要向左旋转一次实现平衡
	AVLNode* RR_Rotation(AVLNode *subRoot) {
		AVLNode* temp = subRoot->rChild;
		subRoot->rChild = temp->lChild;
		temp->lChild = subRoot;
		//完成旋转操作之后,该处分支结点(原为subRoot)发生了变化,
		//因此要返回新的分支节点指针供其父节点更新孩子指针
		return temp;
	}
复制代码

下面两种情况可看作是对根节点和子节点进行上两种旋转操作的组合

RL平衡旋转(先右后左双旋转)

  由于在A的右孩子(R)的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。
  RL型调整的一般形式如下图5所示,表示在A的右孩子B的左子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:
  ①将B的左孩子C提升为新的根结点;
  ②将原来的根结点A降为C的左孩子;
  ③各子树按大小关系连接(AL和BR不变,CL和CR分别调整为A的右子树和B的左子树)。

            图5 一般形式的RL型调整
            图6 RL旋转实例

	/// RL平衡旋转(先右后左双旋转)
	//在右孩子(R)的左子树(L)上插入导致不平衡,需要先对分支结点的右孩子进行一次右旋(LL_Rotation),
	//再对分支结点进行一次左旋(RR_Rotation)
	AVLNode* RL_Rotation(AVLNode *subRoot) {
		//对subRoot右孩子结点LL旋转后,更新subRoot右结点指针
		subRoot->rChild=LL_Rotation(subRoot->rChild);  
		return RR_Rotation(subRoot);//返回新的分支结点供原分支节点的父节点更新孩子指针
	}
复制代码

LR平衡旋转(先左后右双旋转)

  由于在A的左孩子(L)的右子树(R)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1变为2。
  LR型调整的一般形式如下图7所示,表示在A的左孩子B的右子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:
  ①将B的左孩子C提升为新的根结点;
  ②将原来的根结点A降为C的右孩子;
  ③各子树按大小关系连接(BL和AR不变,CL和CR分别调整为B的右子树和A的左子树)。

           图7 一般形式的LR型调整
           图8 LR旋转实例

	/// LR平衡旋转(先左后右双旋转)
	//在左孩子(L)的右子树(R)上插入导致不平衡,需要先对分支结点的左孩子进行一次左旋(RR_Rotation),
	//再对分支结点进行一次右旋(LL_Rotation)
	AVLNode* LR_Rotation(AVLNode *subRoot) {
		//对subRoot左结点RR旋转后,更新subRoot左结点指针
		subRoot-> lChild = RR_Rotation(subRoot->lChild);  
		return LL_Rotation(subRoot);//返回新的分支结点供原分支节点的父节点更新孩子指针
	}
复制代码

2.3.2 返回结点的平衡因子

  平衡因子=左子树高度-右子树高度

	///返回某个节点的平衡因子
	int BalanceFactor(AVLNode *p) {
		return Height(p->lChild) - Height(p->rChild);
	}
复制代码

2.3.3 平衡某结点为根节点的子树

  对某个结点进行平衡操作,需根据平衡因子分别调用上面实现的四种旋转操作。

	///对某个结点进行平衡操作(根据平衡因子调用四种不同的旋转操作)
	AVLNode* Balancee(AVLNode* subRoot) {
		int bf = BalanceFactor(subRoot);
		if (bf > 1) //左子树更高
		{
			if (BalanceFactor(subRoot->lChild) > 0)
				//左孩子结点平衡因子>0说明新节点多在了左子树上,因此调用LL_Rotation
				subRoot = LL_Rotation(subRoot);
			else
				//左孩子结点平衡因子<0说明新节点多在了右子树上,因此调用LR_Rotation
				subRoot = LR_Rotation(subRoot);
		}
		else if (bf < -1) //右子树更高
		{
			if (BalanceFactor(subRoot->rChild) > 0)
		        //右孩子结点平衡因子>0说明新节点多在了左子树上,因此调用RL_Rotation
				subRoot = RL_Rotation(subRoot);
			else
			    //右孩子结点平衡因子<0说明新节点多在了右子树上上,因此调用RR_Rotation
				subRoot = RR_Rotation(subRoot);
		}
		//对分支结点进行平衡操作后可能会更新该分支节点,故将新的分支结点返回供原父结点更新孩子指针
		return subRoot;
	}
复制代码

2.3.4 返回某结点为根节点的子树高度

  使用递归求树的高度

	///返回某结点为根节点的子树高度
	int Height(AVLNode *p) {
		if (p == NULL)
			return 0;
		int i = Height(p->lChild);
		int j = Height(p->rChild);
		return i > j ? i + 1 : j + 1;
	}
复制代码

2.3.5 返回子树最大/小值结点指针

  最大值位于树的最右端,最小值位于树的最左端。

	///返回子树最大值结点的指针
	AVLNode* TreeMax(AVLNode* subTree) {
		if (!subTree)
			return NULL;
		while (subTree->rChild) {
			subTree = subTree->rChild;
		}
		return subTree;
	}
	///返回子树最小值结点的指针
	AVLNode* TreeMin(AVLNode* subTree) {
		if (!subTree)
			return NULL;
		while (subTree->lChild) {
			subTree = subTree->lChild;
		}
		return subTree;
	}
复制代码

2.3.6 插入值为x的结点

  插入操作视为根据x和分支结点的比较结果,在不同的子树上进行插入并进行平衡操作,并不断更新该子树根节点的父节点的孩子指针。

	///简化版插入函数
	void Insert(const int x) {
		root = Insert(root, x);
	}
	///插入(视为在某结点为根节点的子树上进行插入)
	//对子树上进行插入操作后都需要平衡操作,可能会改变该子树的根节点,
	//因此设置返回值记录完成操作后子树的根结点指针)
	AVLNode* Insert(AVLNode* subRoot, const int k) {
		if (subRoot == NULL) {
			subRoot = new AVLNode(k);
		}
		else if (k > subRoot->data) //需要在右子树上插入新的结点
		{
			subRoot->rChild = Insert(subRoot->rChild, k);
			//在右子树上插入结点后可能导致不平衡,故需要对右子树进行平衡操作
			//而平衡操作可能会导致子树根结点产生变化,故需更新当前的子树根节点
			subRoot = Balancee(subRoot);
		}
		else if (k < subRoot->data) { //需要在左子树上插入新的结点
			subRoot->lChild = Insert(subRoot->lChild, k);
			//和上面同理
			subRoot = Balancee(subRoot);
		}
		//将新的子树根结点指针返回供原父节点更新孩子指针
		return subRoot;
	}
复制代码

2.3.7 创建二叉树函数(从控制台接收数值)

  从栈获取,数组中获取等构造方式都是依次调用Insert()函数实现,因不是本文核心这里不作列举。

	///从命令行接受数据创建AVL树。使用其他方式创建原理一致
	void Create() {
		cout << "input numbers to create AVL: " << endl;
		int temp;
		while (cin >> temp) {
			root=Insert(root, temp);
		}
		cout << "AVL创建完成!" << endl;
		Print();
	}
复制代码

2.3.8 删除结点

  删除操作是AVL实现中的重难点!
  本文通过对删除结点所处的位置进行分类讨论,使用递归的形式实现,删除结点时需要分类讨论的情况如下:

  • 情况1:要删除的就是该树的根节点
      1) 该树的左右子树都存在:
      > 左子树高于右子树,则根节点的值替换为其直接前驱的值,然后通过递归调用转化为删除其直接前驱(其位于左子树上,也就意味着去降低左子树高度)并平衡该根结点;
      > 右子树高于左子树,则根节点的值替换为其直接后继的值,然后通过递归调用转化为删除其直接后继(其位于右子树上,也就意味着去降低右子树高度) 并平衡该根结点;
      2) 只存在一颗子树,使用孩子结点替换根节点,并平衡该结点;
      3) 左右子树都不存在,根节点置为NULL;[ 2) ,3)代码实现时可以合并]
  • 情况2:要删除的节点位于左子树上则递归调用---即对根结点的左子树上进行删除操作,并平衡该子树;
  • 情况3:要删除的节点位于右子树上---在根结点的右子树上进行删除操作,并平衡该该子树;
	///简化版删除函数
	void Remove(int x) {
		root=Remove(root, x);
	}
	///删除结点(视为在该结点为根节点的树上进行删除操作)
	AVLNode* Remove(AVLNode* subRoot, int x) {
		if (!Search(x)) {//不存在x的结点则直接返回
			cout << "不存在值为" << x << "的结点!" << endl;
			return root;
		}

		if (!root)  //root为空指针都直接返回NULL
			return root;
			
		if (subRoot->data == x)  //情况1:要删除的就是该树的根节点
		{
			if (subRoot->lChild && subRoot->rChild)//情况1.1:该树的左右子树都存在
			{
				if (BalanceFactor(subRoot)>0) 
				{
					//左子树高于右子树,则根节点的值替换为其直接前驱的值,然后转化为删除
					//其直接前驱(其位于左子树上,也就意味着去降低左子树高度)
					AVLNode *tmp = TreeMax(subRoot->lChild); //直接前驱就是左子树的最大值
					subRoot->data = tmp->data;
					//递归调用Remove()删除subRoot的左子树上的前驱结点后,Remove()返回可能为
					//新的subRoot的左子树根节点供subRoot更新左孩子结点((Remove()会调用Balance()函数平衡其操作的树))。
					subRoot->lChild = Remove(subRoot->lChild, tmp->data);
				}
				else {
					//右子树高于左子树,则根节点的值替换为其直接后继的值,
					//然后转化为删除其直接后继(其位于右子树上,也就意味着去降低右子树高度)
					AVLNode *tmp = TreeMin(subRoot->rChild);
					subRoot->data = tmp->data;
					subRoot->rChild = Remove(subRoot->rChild, tmp->data);
				}
			}
			else //情况1.2:只存在一颗子树或者都不存在
			{
				//直接将根节点更新为其孩子结点(都不存在则为NULL)
				AVLNode * tmp = subRoot;
				subRoot = (subRoot->lChild) ? (subRoot->lChild) : (subRoot->rChild);
				delete tmp;
				tmp = NULL;
			}
		}
		else if (x < subRoot->data) { //情况2:要删除的节点位于左子树上
			//递归调用,在subRoot的左子树上进行删除操作,并返回新的左子树根节点供subRoot更新左孩子指针
			subRoot->lChild = Remove(subRoot->lChild, x);
			//在subRoot的左子树上完成删除操作后,可能导致该树不平衡,故需要进行平衡操作并更新当前根节点
			if (BalanceFactor(subRoot) < -1)
				subRoot = Balancee(root);
		}
		else {//情况3:要删除的节点位于右子树上
			//递归调用,在subRoot的右子树上进行删除操作,并返回新的右子树根节点供subRoot更新右孩子指针
			subRoot->rChild = Remove(subRoot->rChild, x);
			//在subRoot的右子树上完成删除操作后,可能导致该树不平衡,故需要进行平衡操作并更新当前根节点
			if (BalanceFactor(subRoot) >1)
				subRoot = Balancee(subRoot);
		}
		//返回该树当前根节点供其父节点更新孩子节点
		return subRoot;
	}
复制代码

2.3.9 返回结点值为x的结点指针

  依次和分支节点对比直到找到到值为x的结点.

	///返回值为x的结点指针
	AVLNode* Search(int x) {
		AVLNode *p = root;
		while (p) {
			if (p->data == x)
				break;
			else if (p->data < x)
				p = p->rChild;
			else
				p = p->lChild;
		}
		return p;
	}
复制代码

2.3.10 输出二叉树

  使用中序遍历和先序遍历输出二叉树。若对二叉树的先序、中序、后续遍历的递归和非递归算法感兴趣,请查阅:二叉树成员函数代码实现及简单原理分析

	///输出该排序树
	void Print() {
		cout << "中序遍历为: ";
		MidOrder(root);
		cout << endl;
		cout << "先序遍历为: ";
		PreOrder(root);
		cout << endl;
	}

	///先序遍历输出
	void PreOrder(AVLNode *p) {
		if (p != NULL) {
			cout << p->data << " ";
			PreOrder(p->lChild);
			PreOrder(p->rChild);
		}
	}
	///中序遍历输出。
	void MidOrder(AVLNode *p) {
		if (p != NULL) {
			MidOrder(p->lChild);
			cout << p->data << " ";
			MidOrder(p->rChild);
		}
	}
复制代码

2.3.11 销毁二叉树

使用递归形式完成销毁操作

	///销毁该树
	void destroy(AVLNode* p) {
		if (p) {
			destroy(p->lChild);
			destroy(p->rChild);
			delete p;
		}
	}
复制代码

2.4 完整代码

/*******平衡二叉树(AVL)***********/
#pragma once
#include<iostream>
using namespace std;

///平衡二叉树结点结构
struct AVLNode {
	int data;
	AVLNode *lChild, *rChild;
	AVLNode(int a) {
		data = a;
		lChild = NULL;
		rChild = NULL;
	}
};

class AVLTree {
public:
	AVLTree() {
		root = NULL;
	}
	~AVLTree() {
		destroy(root);
	}

	///从命令行接受数据创建AVL树。使用其他方式创建原理一致
	void Create() {
		cout << "input numbers to create AVL: " << endl;
		int temp;
		while (cin >> temp) {
			root=Insert(root, temp);
		}
		cout << "AVL创建完成!" << endl;
		Print();
	}

	///返回某结点为根节点的子树高度
	int Height(AVLNode *p) {
		if (p == NULL)
			return 0;
		int i = Height(p->lChild);
		int j = Height(p->rChild);
		return i > j ? i + 1 : j + 1;
	}

	///输出该排序树
	void Print() {
		cout << "中序遍历为: ";
		MidOrder(root);
		cout << endl;
		cout << "先序遍历为: ";
		PreOrder(root);
		cout << endl;
	}

	///先序遍历输出
	void PreOrder(AVLNode *p) {
		if (p != NULL) {
			cout << p->data << " ";
			PreOrder(p->lChild);
			PreOrder(p->rChild);
		}
	}
	///中序遍历输出
	void MidOrder(AVLNode *p) {
		if (p != NULL) {
			MidOrder(p->lChild);
			cout << p->data << " ";
			MidOrder(p->rChild);
		}
	}

	///简化版插入函数
	void Insert(const int x) {
		root = Insert(root, x);
	}

	///简化版删除函数
	void Remove(int x) {
		root=Remove(root, x);
	}

	///返回值为x的结点指针
	AVLNode* Search(int x) {
		AVLNode *p = root;
		while (p) {
			if (p->data == x)
				break;
			else if (p->data < x)
				p = p->rChild;
			else
				p = p->lChild;
		}
		return p;
	}

	///返回子树最大值结点的指针
	AVLNode* TreeMax(AVLNode* subTree) {
		if (!subTree)
			return NULL;
		while (subTree->rChild) {
			subTree = subTree->rChild;
		}
		return subTree;
	}

	///返回子树最小值结点的指针
	AVLNode* TreeMin(AVLNode* subTree) {
		if (!subTree)
			return NULL;
		while (subTree->lChild) {
			subTree = subTree->lChild;
		}
		return subTree;
	}

private:
	///根节点
	AVLNode *root;

	///插入(视为在某结点为根节点的子树上进行插入)
	//对子树上进行插入操作后都需要平衡操作,可能会改变该子树的根节点,
	//因此设置返回值记录完成操作后子树的根结点指针)
	AVLNode* Insert(AVLNode* subRoot, const int k) {
		if (subRoot == NULL) {
			subRoot = new AVLNode(k);
		}
		else if (k > subRoot->data) //需要在右子树上插入新的结点
		{
			subRoot->rChild = Insert(subRoot->rChild, k);
			//在右子树上插入结点后可能导致不平衡,故需要对右子树进行平衡操作
			//而平衡操作可能会导致子树根结点产生变化,故需更新当前的子树根节点
			subRoot = Balancee(subRoot);
		}
		else if (k < subRoot->data) { //需要在左子树上插入新的结点
			subRoot->lChild = Insert(subRoot->lChild, k);
			//和上面同理
			subRoot = Balancee(subRoot);
		}
		//将新的子树根结点指针返回供原父节点更新孩子指针
		return subRoot;
	}

	///删除结点(视为在该结点为根节点的树上进行删除操作)
	AVLNode* Remove(AVLNode* subRoot, int x) {
		if (!Search(x)) {//不存在x的结点则直接返回
			cout << "不存在值为" << x << "的结点!" << endl;
			return root;
		}

		if (!root)  //root为空指针都直接返回NULL
			return root;

		if (subRoot->data == x)  //情况1:要删除的就是该树的根节点
		{
			if (subRoot->lChild && subRoot->rChild)//情况1.1:该树的左右子树都存在
			{
				if (BalanceFactor(subRoot)>0) 
				{
					//左子树高于右子树,则根节点的值替换为其直接前驱的值,然后转化为删除
					//其直接前驱(其位于左子树上,也就意味着去降低左子树高度)
					AVLNode *tmp = TreeMax(subRoot->lChild); //直接前驱就是左子树的最大值
					subRoot->data = tmp->data;
					//递归调用Remove()删除subRoot的左子树上的前驱结点后,Remove()返回可能为
					//新的subRoot的左子树根节点供subRoot更新左孩子结点((Remove()会调用Balance()函数平衡其操作的树))。
					subRoot->lChild = Remove(subRoot->lChild, tmp->data);
				}
				else {
					//右子树高于左子树,则根节点的值替换为其直接后继的值,
					//然后转化为删除其直接后继(其位于右子树上,也就意味着去降低右子树高度)
					AVLNode *tmp = TreeMin(subRoot->rChild);
					subRoot->data = tmp->data;
					subRoot->rChild = Remove(subRoot->rChild, tmp->data);
				}
			}
			else //情况1.2:只存在一颗子树或者都不存在
			{
				//直接将根节点更新为其孩子结点(都不存在则为NULL)
				AVLNode * tmp = subRoot;
				subRoot = (subRoot->lChild) ? (subRoot->lChild) : (subRoot->rChild);
				delete tmp;
				tmp = NULL;
			}
		}
		else if (x < subRoot->data) { //情况2:要删除的节点位于左子树上
			//递归调用,在subRoot的左子树上进行删除操作,并返回新的左子树根节点供subRoot更新左孩子指针
			subRoot->lChild = Remove(subRoot->lChild, x);
			//在subRoot的左子树上完成删除操作后,可能导致该树不平衡,故需要进行平衡操作并更新当前根节点
			if (BalanceFactor(subRoot) < -1)
				subRoot = Balancee(root);
		}
		else {//情况3:要删除的节点位于右子树上
			//递归调用,在subRoot的右子树上进行删除操作,并返回新的右子树根节点供subRoot更新右孩子指针
			subRoot->rChild = Remove(subRoot->rChild, x);
			//在subRoot的右子树上完成删除操作后,可能导致该树不平衡,故需要进行平衡操作并更新当前根节点
			if (BalanceFactor(subRoot) >1)
				subRoot = Balancee(subRoot);
		}
		//返回该树当前根节点供其父节点更新孩子节点
		return subRoot;
	}

	///返回某个节点的平衡因子
	int BalanceFactor(AVLNode *p) {
		return Height(p->lChild) - Height(p->rChild);
	}

	///对某个结点进行平衡操作(根据平衡因子调用四种不同的旋转操作)
	AVLNode* Balancee(AVLNode* subRoot) {
		int bf = BalanceFactor(subRoot);
		if (bf > 1) //左子树更高
		{
			if (BalanceFactor(subRoot->lChild) > 0)
				//左孩子结点平衡因子>0说明新节点多在了左子树上,因此调用LL_Rotation
				subRoot = LL_Rotation(subRoot);
			else
				//左孩子结点平衡因子<0说明新节点多在了右子树上,因此调用LR_Rotation
				subRoot = LR_Rotation(subRoot);
		}
		else if (bf < -1) //右子树更高
		{
			if (BalanceFactor(subRoot->rChild) > 0)
		        //右孩子结点平衡因子>0说明新节点多在了左子树上,因此调用RL_Rotation
				subRoot = RL_Rotation(subRoot);
			else
			    //右孩子结点平衡因子<0说明新节点多在了右子树上上,因此调用RR_Rotation
				subRoot = RR_Rotation(subRoot);
		}
		//对分支结点进行平衡操作后可能会更新该分支节点,故将新的分支结点返回供原父结点更新孩子指针
		return subRoot;
	}
	/*****************************
	          四种旋转操作
	******************************/
	/// LL平衡旋转(右单旋转)
	//在左孩子(L)的左子树(L)上插入导致不平衡,需要向右旋转一次实现平衡
	AVLNode* LL_Rotation(AVLNode *subRoot) {
		AVLNode* temp = subRoot->lChild;
		subRoot->lChild = temp->rChild;
		temp->rChild = subRoot;
		//完成旋转操作之后,该处分支结点(原为subRoot)发生了变化,
		//因此要返回新的分支节点指针供其父节点更新孩子指针
		return temp;
	}
	/// RR平衡旋转(左单旋转)
	//在右孩子(R)的右子树(R)上插入导致不平衡,需要向左旋转一次实现平衡
	AVLNode* RR_Rotation(AVLNode *subRoot) {
		AVLNode* temp = subRoot->rChild;
		subRoot->rChild = temp->lChild;
		temp->lChild = subRoot;
		//完成旋转操作之后,该处分支结点(原为subRoot)发生了变化,
		//因此要返回新的分支节点指针供其父节点更新孩子指针
		return temp;
	}
	/***********下面两种情况可看作是对根节点和子节点进行上两种旋转操作的组合*****************/
	/// RL平衡旋转(先右后左双旋转)
	//在右孩子(R)的左子树(L)上插入导致不平衡,需要先对分支结点的右孩子进行一次右旋(LL_Rotation),
	//再对分支结点进行一次左旋(RR_Rotation)
	AVLNode* RL_Rotation(AVLNode *subRoot) {
		//对subRoot右孩子结点LL旋转后,更新subRoot右结点指针
		subRoot->rChild=LL_Rotation(subRoot->rChild);  
		return RR_Rotation(subRoot);//返回新的分支结点供原分支节点的父节点更新孩子指针
	}
	/// LR平衡旋转(先左后右双旋转)
	//在左孩子(L)的右子树(R)上插入导致不平衡,需要先对分支结点的左孩子进行一次左旋(RR_Rotation),
	//再对分支结点进行一次右旋(LL_Rotation)
	AVLNode* LR_Rotation(AVLNode *subRoot) {
		//对subRoot左结点RR旋转后,更新subRoot左结点指针
		subRoot-> lChild = RR_Rotation(subRoot->lChild);  
		return LL_Rotation(subRoot);//返回新的分支结点供原分支节点的父节点更新孩子指针
	}

	///销毁该树
	void destroy(AVLNode* p) {
		if (p) {
			destroy(p->lChild);
			destroy(p->rChild);
			delete p;
		}
	}
};
复制代码

3 测试代码(main.cpp)

   ///8.平衡二叉树
    AVLTree avl;
	avl.Create();
	int iterTimes=9;
	cout << "删除测试:" << endl;
	for (int i = 1; i < iterTimes; i++) {
		avl.Remove(i);
		cout <<"删除 "<< i << " 后: "<<endl;
		avl.Print();
	}
	cout << endl;
复制代码

输入:8 3 1 4 7 5 2 6 ^Z //^Z为ctrl+z,表示输入中止
输出:

4 参考资料

  1. 平衡二叉树:blog.csdn.net/isunbin/art…
  2. STL源码笔记(18)—平衡二叉树AVL(C++封装+模板):blog.csdn.net/zhangxiao93…
  3. 百度百科·平衡树:baike.baidu.com/item/平衡树/76…
关注下面的标签,发现更多相似文章
评论