AVL tree: height balanced binary search tree

AVL tree

  1. The relationship between AVL tree and BST tree
      Answer: BST tree (binary sort tree) is a single tree when the data keys of the nodes are in order, and the efficiency of the search is directly reduced to O(N) instead of the tree height. In order to make the tree as even as possible on both sides, the AVL tree is designed.The difference between the left and right heights of the AVL tree does not exceed 1
  2. Why can't the height difference between the left and right of the AVL tree be perfect and equal?
      Answer: It's not because I don't want to, it's because I can't do it, because in many cases when inserting nodes, there may be a difference between the left and right sides. In addition, because the balance factor of the AVL tree is kept at 0 or 1, the AVL tree is evenly balanced in shape, avoiding the single-branch situation , and the time complexity of the search remains O(logN).

accomplish

1. Member structure

  • Tree node:
      Each node of the AVL tree is a triple chain. Inserting a node, AVL tree, needs to check the balance factor, and the balance factor needs to be updated upwards. If the balance factor of the parent node becomes 0 after insertion, there is no need to continue to update upwards, because the balance factor 0 will definitely not affect the upward node. If the node is inserted so that the balance factor of the parent node becomes 1, then continue to update upwards, and stop when it finds a node with 2, and start to consider rotation, because the part is already unbalanced, and it is meaningless to update it again.
    Summarize:
  1. Each node of the AVL tree has a balance factor (bf), and bf keeps the AVL tree balanced. So a member variable is needed: bf.
  2. Inserting or deleting the current node for a node will affect the balance factor of the entire or partial tree from the bottom up, and affect the bf of the parent node, so the AVL tree node needs to update the bf value of the parent node from the current upward, and needs to find the parent node, so three pointers are required .
  3. Each node needs to store KV pairs.
  4. Constructor, each node needs to initialize bf to 0.
  5. AVL can also be achieved without a balance factor, but it is more troublesome.

code show as below:

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 tree class
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;

Ways to implement an AVL tree:

  • Insert insert(): parameter: KV key-value pair: const pair<K, V>& kv

step:

  1. First judge whether the current tree has a root, if not, use the inserted node as the root.
  2. Contrary to 1, there is already a root, and now the inserted node is not the root, then according to BST (Binary Search Tree), we need to find a suitable leaf node position for insertion. so:

a. Set parent = nullptr, cur = _root, if the interpolation value is large, move cur to the right, and update parent at the same time, if the interpolation value is small, cur will move to the left. while() seeks until it stops.
b. If equality is found, return false, meaning no insertion

  1. If there is no return value in the previous step, it means that the appropriate leaf node position has been reached, then judge whether cur is on the left or right of the parent, and then directly insert this node, and the parent links to this node . The above is still the old operation of BST,The following is the core of AVL: the operation of updating the balance factor balance factor (bf).

  2. Update the balance factor . If parent==nullptr, it means that we are inserting the root node, so there is no need to do it. For a single node, bf must be 0 and it has been initialized to 0 in the node's constructor. So the main operations are placed in while(parent != nullptr), that is to say, after inserting the non-root node, we need to update the bf value of the entire parent and cur cyclically. The reason for the loop is: after inserting a node for the child of cur, the bf of cur and cur’s parent may be +1 or -1, but it needs to be updated upwards, because only when parent->bf is 0 can stop, or parent->bf==2, or stop after rotation . Because after the rotation is adjusted, the parent must be 0, so the rotation is broken. The following discusses in detail the writing method in while(parent!=nullptr).

  3. Just insert the child of cur, determine whether cur is the left child or the right child of the parent, specify the insertion on the left child side, bf–, and the insertion on the right child side, bf++ , inserting the left child and right child of the node is also directly bf– and bf++. If parent->bf == 0, then exit the loop , because it will no longer affect the subtree bf above, and the AVL tree is balanced. However, after bf++ or bf–, the parent's |bf|==2 may be caused, and the balance factor bf is 2, which requires rotation adjustment . The adjustments that need to be made when parent and cur have different bf values ​​are discussed below .

  4. When parent->bf == 0 break, exit the loop, because it will not affect. (In fact, it was mentioned in the above 5, and the discussion will be carried out here)

  5. abs(parent->bf == 1), then update the bf value upwards.

cur = parent;
parent = parent->_parent;
  1. abs(parent->bf == 2),bf==2, you need to rotate and adjust the bf value to make the AVL tree balanced. Below, according to the 4 different situations of the bf values ​​of cur and parent , there are the following 4 rotation strategies: 9, 10, 11, and 12 different situations.

Description :

For the 4 different rotation scenarios, after the node is inserted, the bf (balance factor) of a node (parent) where the balance factor appears is updated upwards and adjusted when the bf (balance factor) is +2 or -2.

Cur is ±1 and parent is ±2 Reasons:

Scenario: Find the child position of a suitable node and insert the node. The child node cur itself is empty, and cur->bf is 0. The parent of cur, parent->bf, may or may not have children. The maximum parent is +1 or -1, and the small point of bf may be 0. If parent->bf is not 0, adjust upwards according to the following logic: cur becomes parent, and cur's bf is +1 or -1 until the parent is +2 or -2. parent is +2 or -2, cur is +1 or -1,
not 0 .

cur = parent;
parent = parent->_parent;

Also, rotation doesn't care who the outer cur is .
9. Left rotation: parameter: parent; effect: parent goes down to the right, cur goes up from right to left;
parent->bf = 2,cur->bf = -1, leading to the right child's right interpolation.
The scenario diagram is as follows:
insert into C, update the bf values ​​of cur and parent, when cur is subR, find that the parent’s bf is 2, after left rotation, let subR be the parent, and the parent continues.
Please add a picture description
According to the situation diagram above, it can be seen that the positions of parent and subR (cur) have changed, and subRL has been given to the parent. There are 3 nodes involved.
step:

  1. Locate the nodes where the relationship between subR, subRL, and parentParent (grandfather, father's father) will change.
  2. Change the relationship between parent and subR.
  3. The relationship between parent and subRL. The subRL is given to the parent. Consider the case where subRL does not exist, such as when there is a single branch.
  4. The son of the grandfather is updated to subR. Considering that the grandfather is empty (that is, the parent is the root node), there is no need to change the point of the child of the grandfather. If the grandfather is not empty, judge whether the chain is left or right.
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. Right rotation : parameter: parent; effect: parent goes down, cur goes up;
    parent->bf = -2,cur->bf = -1, giving the left child the left interpolation results in

Please add a picture description
According to the above situation diagram, it can be seen that the positions of parent and subL(cur) have changed, and subLR has been given to parent. There are 3 nodes involved.
step:

  1. Locate the point to change
  2. SubL and par relationship change
  3. The original subLR was given to par, considering that subLR does not exist, that is, the case of a single branch
  4. Establish the relationship between pP and subL, considering the situation that pP does not exist when the parent is the root
  5. Update bf balance factor: subR (cur), and parent rotate bf are all 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. Left and right double rotation : parameter: parent. (No matter how you turn it, you will find that the absolute value of bf of the current node is 2)

There are three cases:

  • Case 1
    Please add a picture description
  • Case 2:
    Please add a picture description
  • Case 3:
    Please add a picture description

  The above three scenario diagrams all require left and right double rotation. When the left b or c on the right side of the left subtree is inserted, it will cause the parent's bf==2. In addition, abcd is empty, and subLR is a newly inserted node, which will also cause bf==2 of parent. So there are three situations where left and right double rotation is required .
How to distinguish the three left-right double-spin situations?
Use the bf value of subLR to distinguish.

  1. Insert b, the bf of subLR is -1.
  2. Insert c, the bf of subLR is 1.
  3. a, b, c, and d are empty, and bf of subLR is 0. At this time, it is because subLR is a newly inserted node, and the imbalance caused by insertion .

For the imbalance caused by inserting subLR, use subLR->bf as the bf variable to distinguish.

  • Case 1: subLR->bf == -1
    Please add a picture description

After rotation:
subLR->bf = 0 (bf variable)
subL->bf = 0
parent->bf = 1

  • Case 2: subLR->bf == 1
    Please add a picture description
    after rotation:
    subLR->bf = 0 (bf variable)
    subL->bf = -1
    parent->bf = 0

  • Case 3: subLR->bf == 0

Please add a picture description
After rotation:
subLR->bf = 0 (bf variable)
subL->bf = 0
parent->bf = 0

step: ( The above three steps are actually the same )

  1. Determine several changed nodes, parent, subL, subLR
  2. Use subL as the parent to rotate left (call the function directly), so that subLR replaces the position of subL.
  3. Rotate the parent to the right, so that subLR goes to the position of the parent, and the parent goes down to the right.
  4. Make judgments on different bf, and update the three point bf in each case. The difference between each case is that the bf value is different after the rotation .
// 左右双旋
	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. Right and left double rotation : parameter: parent. (No matter how you turn it, you will find that the absolute value of bf of the current node is 2)

The three cases are all due to the imbalance caused by the insertion on the child of subRL. The following three cases are distinguished by the bf value of subRL as the variable bf.

  • Case 1:
    bf(subRL->bf) == 1
    Please add a picture description

  • Case 2:
    bf(subRL->bf) == -1
    Please add a picture description

  • Case 3:
    bf(subRL->bf) == 0
    Please add a picture description

Steps: ( the above three steps are the same )

  1. Determine several changed nodes, parent, subR, subRL
  2. Use subR as the parent to right-rotate (directly call the function), so that subRL replaces the position of subR
  3. Rotate the parent to the left, so that subRL goes to the parent position, and the parent goes down to the right
  4. Make judgments on different bf, because the difference in each case is that the bf value is different after rotation .
// 右左双旋
	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); //在旋转前树的平衡因子就有问题
		}
	}

Verification of AVL tree:

  • Judging AVL tree: AVL tree is a balanced BST tree
  1. First, according to whether the inorder traversal is in ascending order:

  2. Then judge the balance factor of each node. If the absolute value of the height difference between the left and right subtrees of each node of the binary tree does not exceed 11, then the binary tree is a balanced binary tree.In fact, only 2 in the leetcode question is enough: Jianzhi Offer 55 - II. Balanced binary tree
    Based on the tree node depth height() function.

Perfect code:

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

Xiaojiabiyu code

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

full code

I used to rotate to the left, looking for nodes, and judged that I made a mistake.

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

Please add a picture description

Analysis of the performance impact of rotation on the AVL tree + AVL performance analysis

The average search efficiency of the AVL tree is: O(logN), which is the tree height.

  • But does the rotation in its process affect the efficiency of the tree?
      Answer: If the node is 100W, insert a node, rotate at most once, and update the balance factor at most 20 times, and this method makes the search without a single tree, which makes the search efficiency much higher than that of the BST tree.

Guess you like

Origin blog.csdn.net/myscratch/article/details/128492966