AVL树:高度平衡的二叉搜索树

AVL树

  1. AVL树和BST树的联系
      答:BST树(二叉排序树)当节点的数据key有序时是一棵单支树,查找时效率直接降低到O(N)而不是树高,为了使树尽量两边均匀,设计出了AVL树,AVL树的左右高度差不超过1
  2. 为什么AVL树左右高度差不能追求完美至相等?
      答:不是因为不想,是做不到,因为插入节点很多情况下,可能左右两边差一个。此外,因为AVL树的平衡因子保持在0或1,使得AVL树形态上均匀平衡,避免了单支情况,搜索的时间复杂度保持O(logN)。

实现

1. 成员结构

  • 树节点:
      AVL树的每个节点是三叉链。插入一个节点,AVL树,需要查看平衡因子,而平衡因子需要向上更新。如果插入后,父节点的平衡因子变为0,则不需要继续向上更新,因为平衡因子0肯定不会对向上的节点造成影响。如果插入节点使得父节点平衡因子成1,则继续向上更新,发现有2的节点,就停止,开始考虑旋转了,因为局部已不平衡了,再更新也没意义。
    总结:
  1. AVL树的每个节点有平衡因子(bf),bf使得AVL树能保持平衡。所以需要成员变量:bf。
  2. 给某节点插入或删除当前节点会从局部从底向上影响整个或局部树的balance factor,会影响到影响父亲节点的bf,所以AVL树节点需要从当前向上对父亲节点的bf值更新,需要找父亲节点,所以要三个指针
  3. 每个节点需要存储KV对。
  4. 构造函数,每个节点需要初始化bf为0。
  5. 不用平衡因子也能实现AVL,但比较麻烦。

代码如下:

template<class K, class V>
struct AVLTreeNode
{
    
    
	//三叉链
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	//存储的键值对
	pair<K, V> _kv;

	//平衡因子(balance factor)
	int _bf; //右子树高度-左子树高度

	//构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{
    
    }
};
  • AVL树类
template<class K, class V>
class AVLTree
{
    
    
private:
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		: _pRoot(nullptr)
	{
    
    }
	bool Insert(const pair<K, V>& kv);
	bool IsAVLTree();
private:	// 防止暴露根节点
	bool _IsAVLTree(Node* pRoot);
	size_t _Height(Node* pRoot);
	void RotateL(Node* parent);
	void RotateR(Node* parent);
	void RotateRL(Node* parent);
	void RotateLR(Node* parent);
	Node* _root;

实现AVL树的方法:

  • 插入insert(): 参数:KV键值对:const pair<K, V>& kv

步骤:

  1. 先判断当前树是否有根,如果无根,则把插入的节点作根。
  2. 与1对立,已有根,则现在插入节点非根,则按照BST(二叉搜索树)我们要寻找一个合适的叶子节点位置做插入。所以:

a. 设置parent = nullptr,cur = _root ,被插值大,就把cur给右边走,同时更新parent,被插值小,cur就给左边走。while()寻找直到停止。
b. 如果找到了相等,返回false,意思是没做插入

  1. 之前一步如果没返回值,说明来到了合适叶子节点位置,那么就判断cur在parent左还是右,然后直接插入该节点、父亲链接该节点。以上,还是BST的老操作,下面是AVL的核心:更新平衡因子balance factor(bf)的操作。

  2. 更新平衡因子,如果parent==nullptr,则说明我们插入的是根节点,则不用做,对于单节点来说,bf一定为0且在节点的构造函数中已经初始化设置为0了。所以主要的操作都放在while(parent != nullptr)中,也就是说,插入完非根节点后,我们需要循环地更新整个parent和cur的bf值,需要循环的原因是:给cur的孩子插入节点后,可能对cur和cur的parent的bf做了+1或-1,但是还需要向上更新,因为只有更新到parent->bf为0可以停止,或parent->bf==2,或旋转后停止。因为旋转调节后,parent一定为0,所以旋转完break。下面具体讨论,while(parent!=nullptr)中的写法。

  3. 刚刚对cur的孩子做了插入,判断cur是parent的左孩子还是右孩子,规定左孩子侧的插入,bf–,而右孩子侧的插入,bf++,给节点插入左孩子和右孩子也直接是bf–和bf++。若parent->bf == 0 ,则退出循环,因为不会再对上面的子树bf做影响了,该AVL树是平衡的。然而,在bf++或bf–之后可能造成parent的|bf|==2,平衡因子bf为2就需要旋转调整。下面讨论parent和cur不同bf值时需做的调整。

  4. parent->bf == 0时break,退出循环,因为不会影响。(上面5中其实说到过,这里展开讨论)

  5. abs(parent->bf == 1),则向上更新bf值。

cur = parent;
parent = parent->_parent;
  1. abs(parent->bf == 2),bf==2,需要旋转调整bf值使AVL树平衡。以下,根据cur、和parent的bf值4种不同情况,有以下4种:9、10、11、12不同情况的旋转策略。

说明

4种不同旋转的情景,都在插入节点后,向上更新平衡因子出现的某节点(parent)的bf(平衡因子) 为+2或-2时做调整。

cur为±1且parent为±2理由:

情景:找到合适节点的孩子位置插入节点,该孩子节点cur本身为空,cur->bf为0,cur的父亲parent->bf可能有孩子,也可能没有孩子,最多parent为+1或-1,bf小点可能为0,如果parent->bf不为0,遵循下面逻辑向上调整:
cur变为parent,cur的bf为+1或-1,直到parent为+2或-2时,要旋转,所以每次需要旋转,都是parent为+2或-2,cur为+1或-1,不会是0

cur = parent;
parent = parent->_parent;

此外,旋转无需在意外部cur是谁
9. 左旋: 参数:parent;效果:parent到右边下去了,cur从右边向左上来了;
parent->bf = 2,cur->bf = -1,给右孩子的右侧插导致。
情境图如下:
给C插入,更新cur和parent的bf值,cur为subR时,发现parent的bf为2,左旋后,让subR做parent,parent下去。
请添加图片描述
按如上情境图,可知parent、subR(cur)位置变化了,且subRL给了parent。涉及3个节点。
步骤:

  1. 定位subR、subRL、parentParent(祖父,爹之爹)这些关系要发生改变的节点。
  2. 改parent和subR的关系。
  3. parent和subRL的关系。subRL给了parent。考虑subRL不存在的情况,比如单支时。
  4. 祖父的儿子更新为subR,考虑祖父为空时(即parent为根节点),不需要改变祖父的孩子指向。祖父不为空,则判断该链到左还是右。
void RotateL(Node* parent)
	{
    
    
		// 1. 定位要发生变化的节点
		Node* subR = parent->_right;
		Node* subRL = parent->_right;
		Node* parentParent = parent->_parent;

		// 2. parent和subR的关系改变:情境图靠左两节点的关系
		parent->_parent = subR;	// 原始par要下去
		subR->_left = parent;

		// 3. parent和subRL的关系:
		parent->_right = subRL;
		if (subRL)	// subRL可能不存在:特殊如整体是单支的情况下
		{
    
    
			subRL->_parent = parent;
		}

		// 4. 让原始parent->par,即原始祖父的儿子变subR
		if (parentParent == nullptr)	// parent是根时 以祖父可不存在
		{
    
    
			_root = subR;
			subR->_parent = nullptr;	// subR的父亲不再是parent
		}
		else  // 祖父不空	则判断subR作为新儿,在左边还是右
		{
    
    
			if (parent == parentParent->_left)
				parentParent->_left = subR;
			else
				parentParent->_right = subR;
			subR->_parent = parentParent;
		}
		// 更新bf:旋转前,原始subR、parent一个abs(1)、一个abs(2),两个bf都该置0
		subR->_bf = parent->_bf = 0;
	}
  1. 右旋:参数:parent;效果:parent下去,cur上来;
    parent->bf = -2,cur->bf = -1,给左孩子的左侧插导致

请添加图片描述
按如上情境图可知:parent、subL(cur)位置变化了,且subLR给了parent。涉及3个节点。
步骤:

  1. 定位要发生变化的点
  2. subL和par关系变化
  3. 原来的subLR给了par,考虑subLR不存在,即单支的情况
  4. 建立pP和subL的关系,考虑parent是根时 pP不存在的情况
  5. 更新bf平衡因子:subR(cur)、和parent旋转完 bf 都为0了
void RotateR(Node* parent)
	{
    
    
		// 1。定位要发生变化的点
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

		// 2。subL和par关系变化
		subL->_right = parent;
		parent->_parent = subL;

		// 3。原来的subLR给了par,考虑subLR不存在,即单支的情况
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		// 4。建立pP和subL的关系,考虑parent是根时 pP不存在的情况
		if (parentParent == nullptr)
		{
    
    
			_root = subL;
			_root->_parent = nullptr;
		}
		else  // par不是根,且pp存在时
		{
    
    
			if (parent == parentParent->_left)
			{
    
    
				parentParent->_left = subL;
			}
			else  // par原始在pp右侧
			{
    
    
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}
		// 4. 更新平衡因子:subR(cur)、和parent旋转完 bf 都为0了
		subL->_bf = parent->_bf = 0;
	}

  1. 左右双旋:参数:parent。(不管怎么旋,都是发现当前节点的bf绝对值为2了)

有三种情况:

  • 情况1
    请添加图片描述
  • 情况2:
    请添加图片描述
  • 情况3:
    请添加图片描述

  如上三种情境图都需要左右双旋。当给左子树的右侧的左b插或c插,都会导致parent的bf==2。此外,abcd为空,subLR为新插节点,也会导致parent的bf==2。所以有三种情境下需要左右双旋
如何区分三种左右双旋的情况呢?
以subLR的bf值做区分。

  1. 给b插,subLR的bf为-1。
  2. 给c插,subLR的bf为1。
  3. a、b、c、d为空,subLR的bf为0。此时是因为subLR为新插节点,插入导致的不平衡

给subLR做插入导致的不平衡,拿subLR->bf做bf变量区分。

  • 情况1:subLR->bf == -1
    请添加图片描述

旋转完:
subLR->bf = 0(bf变量 )
subL->bf = 0
parent->bf = 1

  • 情况2:subLR->bf == 1
    请添加图片描述
    旋转完:
    subLR->bf = 0(bf变量)
    subL->bf = -1
    parent->bf = 0

  • 情况3:subLR->bf == 0

请添加图片描述
旋转完:
subLR->bf = 0(bf变量)
subL->bf = 0
parent->bf = 0

步骤:(以上三者步骤其实一样

  1. 确定几个被改变的节点,parent、subL、subLR
  2. 以subL为parent左旋(直接调用函数),使得subLR替换subL位置。
  3. 对parent做右旋,使得subLR去parent位置,而parent向右下走。
  4. 对不同bf做判断,更新每种情况的三个点bf,每种情况的区别在旋转完后bf值不一样
// 左右双旋
	void RotateLR(Node* parent)
	{
    
    
		// 拿subL、subLR 该不平衡是因为在subLR的左或右插入造成的
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;	// 给它的儿子节点插入,即增加它孙子的时候  bf变了

		RotateL(subL);	//  以subL左旋 (利用旋转改变位置,虽然当前节点bf不是2)
		RotateR(parent);	//  以父亲右旋 
		
		// 更新bf
		if (bf == 1)
		{
    
    
			parent->_bf = 0;	// 原来的父亲会成为右儿子,bf 为0
			subL->_bf = -1;
			subLR->_bf = 0;	// 不管哪种情况,这个节点bf都会为0
		}
		else if (bf == -1)	// 给LR的左插入造成的
		{
    
    
			parent->_bf = 1;	// 原来的父亲会成为右儿子,bf 为0
			subL->_bf = 0;
			subLR->_bf = 0;	// 不管哪种情况,这个节点bf都会为0
		}
		else if (bf == 0)
		{
    
    
			parent->_bf = 0;	// 原来的父亲会成为右儿子,bf 为0
			subL->_bf = 0;
			subLR->_bf = 0;	// 不管哪种情况,这个节点bf都会为0
		}
		else  // 到这里后 一定错了
		{
    
    
			assert(false);
		}

	}

  1. 右左双旋:参数:parent。(不管怎么旋,都是发现当前节点的bf绝对值为2了)

三种情况均是因为在subRL的孩子上插入导致的不平衡,以subRL的bf值为变量bf区分以下三种情况。

  • 情况一:
    bf(subRL->bf) == 1
    请添加图片描述

  • 情况二:
    bf(subRL->bf) == -1
    请添加图片描述

  • 情况三:
    bf(subRL->bf) == 0
    请添加图片描述

步骤:(以上三者步骤一样

  1. 确定几个被改变的节点,parent、subR、subRL
  2. 以subR为parent右旋(直接调用函数),使得subRL替换subR位置
  3. 对parent做左旋,使得subRL去parent位置,而parent向右下走
  4. 对不同bf做判断,因为每种情况的区别在旋转完bf值不一样
// 右左双旋
	void RotateRL(Node* parent)
	{
    
    
		// 定位改变的节点
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;	// bf是被插入节点的父亲

		// 先右旋subR,因为subR要上去 再左旋parent,parent要下去(对照情境图)
		RotateR(subR);
		RotateL(parent);

		// 更新bf
		if (bf == 1)	// 情景一:
		{
    
    
			subRL->_bf = 0;
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)	// 情景二:
		{
    
    
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else if (bf == 0)	// 情景三:
		{
    
    
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 0;
		}
		else
		{
    
    
			assert(false); //在旋转前树的平衡因子就有问题
		}
	}

AVL树的验证:

  • 判断AVL树:AVL树是平衡的BST树
  1. 先根据中序遍历是否为升序 :

  2. 再判断每个节点平衡因子,二叉树的每个节点的左右子树的高度差的绝对值不超过 11,则二叉树是平衡二叉树。其实leetcode题中只符合2即可:剑指 Offer 55 - II. 平衡二叉树
    以求树节点深度height()函数为基础。

完美代码:

int height(TreeNode* root) {
    
    
        if (root == NULL) {
    
    
            return 0;
        } else {
    
    
            return max(height(root->left), height(root->right)) + 1;
        }
    }

    bool isBalanced(TreeNode* root) {
    
    
        if (root == NULL) {
    
    
            return true;
        } else {
    
    
            return abs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
        }
    }

小家碧玉代码

int height(Node* root)
{
    
    
	if(root==nullptr)
		return 0;
	return max(height(root->left), height(root->right)) + 1;

}

bool _Isbalance(Node* root)
{
    
    
	if(root == nullptr)
		return true;
	int lH = Height(root->_left);
	int rH = Height(root->_right);
	return abs(lH - rh) < 2 && _Isbalance(root->left) && _Isbalance(root->right);
}

完整代码

曾经在左旋,找节点,判断写错了。

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

template<class K, class V>
struct AVLTreeNode
{
    
    
	//三叉链
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	//存储的键值对
	pair<K, V> _kv;

	//平衡因子(balance factor)
	int _bf; //右子树高度-左子树高度

	//构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{
    
    }
};

// AVL: 二叉搜索树 + 平衡因子的限制
template<class K, class V>
class AVLTree
{
    
    
private:
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		: _root(nullptr)
	{
    
    }

	// AVL插 pair KV	每个节点中存的是KV,所以插入也根据KV做插入节点
	/*
	1. 先按BST走 cur走到空 :当前树空,则直接建根节点
	2. 按BST,parent = nullptr,cur = _root,让cur到合适的空位,相等就不插
	3. 判断该位置在父左还是右,其实已经走到了合适位置,直接根据parent和cur的值也能判断出来,链接到正确的L或R位置
	4. 更新平衡因子:
		看的是parent->bf,因为当前刚刚插入,bf == 0
		1. 新增在右 parent->bf++
		2. 新增在左	parent->bf--
		3. 更新后,如果parent->bf == 1 or -1,需要继续往上更新,因为 -1表示左边高,1表示右边高,说明更新前,bf == 0,
		4. 更新后,如果parent->bf == 0,不用更新,说明现在平衡了,填上了矮的那边
		5. 更新后,parent->bf == 2 or -2 说明之前是1或-1 ,parent所在子树需要调整。
		6. 更新后,parent->bf:2 ,不可能,存在说明之前操作就有错
		向上更新到节点bf == 0 或 为2、-2、或 到根


	*/
	bool Insert(const pair<K, V>& kv)
	{
    
    
		if (_root == nullptr)	// AVL为空时,插入当前做根
		{
    
    
			_root = new Node(kv);
			return true;			// 插根则结束
		}
		Node* parent = nullptr;	// 插入非根,则找合适位置做cur,插入节点
		Node* cur = _root;
		while (cur)	// cur到合适位置,且期间要更新parent
		{
    
    
			if (kv.first < cur->_kv.first) //待插入结点的key值小于当前结点的key值
			{
    
    
				//往该结点的左子树走
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)	// 待插入节点key大于当前
			{
    
    
				//往该结点的右子树走
				parent = cur;
				cur = cur->_right;
			}
			else	// 相等已存在不做插入
			{
    
    
				return false;
			}
		}
		// 2. cur不存在,且cunr在合适位置,做插入
		cur = new Node(kv);	// cur的位置找到,创建节点,准备链接到parent的L或R
		if (kv.first < parent->_kv.first)	// 判断链接到L还是R
		{
    
    
			//插入到parent的左边
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
    
    
			//插入到parent的右边
			parent->_right = cur;
			cur->_parent = parent;
		}

		// 3. 更新平衡因子BF:只要cur一直为+1或-1,且parent存在则一直向上更新,但发现parent->bf==2,会做旋转,使bf为0后break退出
		while (cur!=_root)	// parent???
		{
    
    
			if (cur == parent->_right)	// 查看在父亲左还是右插入
			{
    
    
				parent->_bf++;
			}
			else
			{
    
    
				parent->_bf--;
			}

			// 判断是否可以退出或者需要旋转 
			if (parent->_bf == 0)	// 平衡因子0 ,停止更新
				break;
			else if (abs(parent->_bf) == 1)	// 如果父亲bf为1,需要继续向上更新
			{
    
    
				parent = parent->_parent;
				cur = cur->_parent;
			}
			else if (abs(parent->_bf) == 2)	// parent->bf==2 需旋转,且转后 bf == 0 不用更新  
			{
    
    
				// parent2 cur1要左旋	
				if (parent->_bf == 2 && cur->_bf == 1)
					RotateL(parent);

				// -2 -1:右旋
				else if (parent->_bf == -2 && cur->_bf == -1)
					RotateR(parent);

				// par == -2  cur == -1,如画图中 本身左边多了一个,而左儿子的右孩子又多1
				else if (parent->_bf == -2 && cur->_bf == 1)
					RotateLR(parent);

				else if (parent->_bf == 2 && cur->_bf == -1)
					RotateRL(parent);

				break;	// 旋完,bf==0	可以退出
			}
			else  // 其它情况,需要 报错 理论上不会走到这里
			{
    
    
				assert(false);
			}
		}
		// 沿着当前往上更新平衡因子
		return true;	
	}

	// AVL树的验证
	bool IsAVLTree()
	{
    
    
		return _IsAVLTree(_root);
	}

	void InOrder()
	{
    
    
		_InOrder(_root);
	}

private:
	void _InOrder(Node* root)
	{
    
    
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << " : " << root->_kv.second << endl;
		_InOrder(root->_right);
	}


	// 根据AVL树的概念验证pRoot是否为有效的AVL树
	bool _IsAVLTree(Node* pRoot)
	{
    
    
		
		if (pRoot == nullptr)
			return true;
		int lh = _Height(pRoot->_left);
		int rh = _Height(pRoot->_right);
		return abs(lh - rh) < 2 && _IsAVLTree(pRoot->_left) && _IsAVLTree(pRoot->_right);
	}

	size_t _Height(Node* pRoot)
	{
    
    
		if (pRoot == nullptr)
			return 0;
		int lh = _Height(pRoot->_left);
		int rh = _Height(pRoot->_right);
		return max(lh, rh)+1;
	}

	// dragon的代码
	// 左单旋
	void RotateL(Node* parent)
	{
    
    
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		//1、建立subR和parent之间的关系
		parent->_parent = subR;
		subR->_left = parent;

		//2、建立parent和subRL之间的关系
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		//3、建立parentParent和subR之间的关系
		if (parentParent == nullptr)
		{
    
    
			_root = subR;
			subR->_parent = nullptr; //subR的_parent指向需改变
		}
		else
		{
    
    
			if (parent == parentParent->_left)
			{
    
    
				parentParent->_left = subR;
			}
			else //parent == parentParent->_right
			{
    
    
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		//4、更新平衡因子
		subR->_bf = parent->_bf = 0;
	}


	// 右旋
	//右单旋
	void RotateR(Node* parent)
	{
    
    
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

		//1、建立subL和parent之间的关系
		subL->_right = parent;
		parent->_parent = subL;

		//2、建立parent和subLR之间的关系
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		//3、建立parentParent和subL之间的关系
		if (parentParent == nullptr)
		{
    
    
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
    
    
			if (parent == parentParent->_left)
			{
    
    
				parentParent->_left = subL;
			}
			else //parent == parentParent->_right
			{
    
    
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}

		//4、更新平衡因子
		subL->_bf = parent->_bf = 0;
	}


	// LR
	//左右双旋
	void RotateLR(Node* parent)
	{
    
    
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf; //subLR不可能为nullptr,因为subL的平衡因子是1

		//1、以subL为旋转点进行左单旋
		RotateL(subL);

		//2、以parent为旋转点进行右单旋
		RotateR(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
    
    
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
    
    
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 0)
		{
    
    
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
    
    
			assert(false); //在旋转前树的平衡因子就有问题
		}
	}

	
	//右左双旋
	void RotateRL(Node* parent)
	{
    
    
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		//1、以subR为轴进行右单旋
		RotateR(subR);

		//2、以parent为轴进行左单旋
		RotateL(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
    
    
			subRL->_bf = 0;
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)
		{
    
    
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else if (bf == 0)
		{
    
    
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 0;
		}
		else
		{
    
    
			assert(false); //在旋转前树的平衡因子就有问题
		}
	}

	// 左单旋:给右孩子的右边插入导致  需要左旋
	/*	
	只要是parent 2 cur1 就是单纯左旋
		此外,高度如果100w,更新20次平衡因子,其实做的很少,效率还是很高,
		更新不会太耗时,且只会旋转一次,所以旋转也不太耗时,对CPU来说,不算什么
		所以一次插入只会有一次更新,而保持平衡使得树的查找,效率更高,虽然不控制bf,插入简单,但是树的查找效率不行
		AVL树的优势在查找
			// 删除:也要旋转,因为删除可能影响bf
	只有parent和subR平衡因子,收到影响,
	*/ 



	 左单旋
	//void RotateL(Node* parent)
	//{
    
    
	//	// 1. 定位要发生变化的节点
	//	Node* subR = parent->_right;
	//	Node* subRL = subR->_left;
	//	Node* parentParent = parent->_parent;

	//	// 2. parent和subR的关系改变:情境图靠左两节点的关系
	//	parent->_parent = subR;	// 原始par要下去
	//	subR->_left = parent;

	//	// 3. parent和subRL的关系:
	//	parent->_right = subRL;
	//	if (subRL)	// subRL可能不存在:特殊如整体是单支的情况下
	//	{
    
    
	//		subRL->_parent = parent;
	//	}

	//	// 4. 让原始parent->par,即原始祖父的儿子变subR
	//	if (parentParent == nullptr)	// parent是根时 以祖父可不存在
	//	{
    
    
	//		_root = subR;
	//		subR->_parent = nullptr;	// subR的父亲不再是parent
	//	}
	//	else  // 祖父不空	则判断subR作为新儿,在左边还是右
	//	{
    
    
	//		if (parent == parentParent->_left)
	//			parentParent->_left = subR;
	//		else
	//			parentParent->_right = subR;
	//		subR->_parent = parentParent;
	//	}
	//	// 更新bf:旋转前,原始subR、parent一个abs(1)、一个abs(2),两个bf都该置0
	//	subR->_bf = parent->_bf = 0;
	//}

	 右单旋
	//void RotateR(Node* parent)
	//{
    
    
	//	cout << "右旋了" << endl;
	//	// 1。定位要发生变化的点
	//	Node* subL = parent->_left;
	//	Node* subLR = subL->_right;
	//	Node* parentParent = parent->_parent;

	//	// 2。subL和par关系变化
	//	subL->_right = parent;
	//	parent->_parent = subL;

	//	// 3。原来的subLR给了par,考虑subLR不存在,即单支的情况
	//	parent->_left = subLR;
	//	if (subLR)
	//		subLR->_parent = parent;

	//	// 4。建立pP和subL的关系,考虑parent是根时 pP不存在的情况
	//	if (parentParent == nullptr)
	//	{
    
    
	//		_root = subL;
	//		_root->_parent = nullptr;
	//	}
	//	else  // par不是根,且pp存在时
	//	{
    
    
	//		if (parent == parentParent->_left)
	//		{
    
    
	//			parentParent->_left = subL;
	//		}
	//		else  // par原始在pp右侧
	//		{
    
    
	//			parentParent->_right = subL;
	//		}
	//		subL->_parent = parentParent;
	//	}
	//	// 4. 更新平衡因子:subR(cur)、和parent旋转完 bf 都为0了
	//	subL->_bf = parent->_bf = 0;
	//}


	 右左双旋
	//void RotateRL(Node* parent)
	//{
    
    
	//	cout << "右左旋" << endl;
	//	// 定位改变的节点
	//	Node* subR = parent->_right;
	//	Node* subRL = subR->_left;
	//	int bf = subRL->_bf;	// bf是被插入节点的父亲

	//	// 先右旋subR,因为subR要上去 再左旋parent,parent要下去(对照情境图)
	//	RotateR(subR);
	//	RotateL(parent);

	//	// 更新bf
	//	if (bf == 1)	// 情景一:
	//	{
    
    
	//		subRL->_bf = 0;
	//		parent->_bf = -1;
	//		subR->_bf = 0;
	//	}
	//	else if (bf == -1)	// 情景二:
	//	{
    
    
	//		subRL->_bf = 0;
	//		parent->_bf = 0;
	//		subR->_bf = 1;
	//	}
	//	else if (bf == 0)	// 情景三:
	//	{
    
    
	//		subRL->_bf = 0;
	//		parent->_bf = 0;
	//		subR->_bf = 0;
	//	}
	//	else
	//	{
    
    
	//		assert(false); //在旋转前树的平衡因子就有问题
	//	}

	//}

	 左右双旋
	//void RotateLR(Node* parent)
	//{
    
    
	//	cout << "左右旋转" << endl;
	//	// 拿subL、subLR 该不平衡是因为在subLR的左或右插入造成的
	//	Node* subL = parent->_left;
	//	Node* subLR = subL->_right;
	//	int bf = subLR->_bf;	// 给它的儿子节点插入,即增加它孙子的时候  bf变了

	//	RotateL(subL);	//  以subL左旋 (利用旋转改变位置,虽然当前节点bf不是2)
	//	RotateR(parent);	//  以父亲右旋 
	//	
	//	// 更新bf
	//	if (bf == 1)
	//	{
    
    
	//		parent->_bf = 0;	// 原来的父亲会成为右儿子,bf 为0
	//		subL->_bf = -1;
	//		subLR->_bf = 0;	// 不管哪种情况,这个节点bf都会为0
	//	}
	//	else if (bf == -1)	// 给LR的左插入造成的
	//	{
    
    
	//		parent->_bf = 1;	// 原来的父亲会成为右儿子,bf 为0
	//		subL->_bf = 0;
	//		subLR->_bf = 0;	// 不管哪种情况,这个节点bf都会为0
	//	}
	//	else if (bf == 0)
	//	{
    
    
	//		parent->_bf = 0;	// 原来的父亲会成为右儿子,bf 为0
	//		subL->_bf = 0;
	//		subLR->_bf = 0;	// 不管哪种情况,这个节点bf都会为0
	//	}
	//	else  // 到这里后 一定错了
	//	{
    
    
	//		assert(false);
	//	}
	//}

private:
	Node* _root;
};

请添加图片描述

分析旋转对AVL树的性能影响+AVL性能分析

AVL树的查找效率平均为:O(logN),也就是树高。

  • 但它的过程中的旋转是否会影响树的效率?
      答:如果节点为100W,插入一个节点,最多旋转一次,而更新最多更新20次平衡因子,而这样的做法使得查找时,没有单支树的情况,使得比BST树查找的效率提高了很多。

猜你喜欢

转载自blog.csdn.net/myscratch/article/details/128492966