Detailed explanation of AVL tree

1. The concept of AVL tree

AVL tree is also called balanced binary search tree. Although the binary search tree can shorten the search efficiency, if the data is ordered or close to the ordered binary search tree, it will degenerate into a single tree. Finding an element is equivalent to searching for an element in the sequence table, which is inefficient. Therefore, two Russian mathematicians GMAdelson-Velskii and EMLandis in 1962 invented a method to solve the above problem: when a new node is inserted into the binary search tree, if the left and right subtrees of each node can be guaranteed The absolute value of the height difference does not exceed 1 (need to adjust the nodes in the tree), the height of the tree can be reduced, thereby reducing the average search length. The picture below shows the AVL tree: it
Insert picture description here
satisfies the following properties:

  1. Its left and right subtrees are AVL trees
  2. The absolute value of the difference between the height of the left and right subtrees (referred to as the balance factor) does not exceed 1 (-1/0/1)
  3. If it has n nodes, its height can be kept at log2n, and the search time complexity is O(log2n)

2. Definition of AVL tree node

template<class T>
struct AVLTreeNode
{
    
    
    AVLTreeNode(const T& data)
    : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
    , _data(data), _bf(0)
    {
    
    }
    AVLTreeNode<T>* _pLeft; // 该节点的左孩子
    AVLTreeNode<T>* _pRight; // 该节点的右孩子
    AVLTreeNode<T>* _pParent; // 该节点的双亲
    T _data;
    int _bf; // 该节点的平衡因子
};

3. Insertion of AVL tree

The AVL tree introduces a balance factor on the basis of the binary search tree, so the AVL tree can also be regarded as a binary search tree. Then the AVL tree insertion process can be divided into two steps:

  1. Insert new nodes according to the binary search tree
  2. Adjust the balance factor of the node
bool Insert(const T& data)
{
    
    
    // 1. 先按照二叉搜索树的规则将节点插入到AVL树中
    // 2. 新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否破坏了AVL树的平衡性
    /*
    pCur插入后,pParent的平衡因子一定需要调整,在插入之前,pParent
    的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:
    1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可
    2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可
    
    此时:pParent的平衡因子可能有三种情况:0,正负1, 正负2
    1. 如果pParent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插     入后被调整成0,此时满足AVL树的性质,插入成功
    2. 如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更新成正负1,此时以pParent为根的树的高度增加,需要继续向上更新
    3. 如果pParent的平衡因子为正负2,则pParent的平衡因子违反平衡树的性质,需要对其进行旋转处理
    */

    if (nullptr == _root)
	{
    
    
		_root = new Node(data);
		return true;
	}

	// 非空
	// 1. 按照二叉搜索树的规则插入新节点
	// 找新节点在树中的插入位置,并记录其双亲
	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
    
    
		parent = cur;
		if (data < cur->data)
			cur = cur->left;
		else if (data>cur->data)
			cur = cur->right;
		else
			return false;
	}

	// 将新节点插入的位置已经找到了---插入新节点
	cur = new Node(data);
	if (data < parent->data)
		parent->left = cur;
	else
		parent->right = cur;
	cur->parent = parent;

	while (parent)
	{
    
    
		// 新节点插入之后,AVL树的平衡性可能会遭到破坏
		// 必须要更新平衡因子
		if (cur == parent->left)
			parent->bf--;
		else
			parent->bf++;

		if (0 == parent->bf)
			return true;
		else if (1 == parent->bf || -1 == parent->bf)
		{
    
    
			cur = parent;
			parent = cur->parent;
		}
		else
		{
    
    
			// parent的平衡因子可能为:2 || -2
			if (2 == parent->bf)
			{
    
    
				// 右子树高--->最终旋转是左单旋
				if (1 == cur->bf)
					RotateLeft(parent);
				else
					RotateRL(parent);
			}
			else
			{
    
    
				// 左子树高---->最终旋转是右单旋
				if (-1 == cur->bf)
					RotateRight(parent);
				else
					RotateLR(parent);
			}
			break;
		}
	}
	return true;
}

4. AVL tree rotation

If a new node is inserted into an AVL tree that is originally balanced, it may cause imbalance. At this time, the structure of the tree must be adjusted to balance it. According to the different node insertion positions, the rotation of the AVL tree is divided into four types:

4.1 Right single spin

The new node is inserted to the left of the higher left subtree.
Insert picture description here
In the figure above, before insertion, the AVL tree is balanced, the new node is inserted into the left subtree of 30 (note: this is not the left child), and the left subtree of 30 is added by one layer, resulting in an unbalanced binary tree rooted at 60 , To balance 60, you can only reduce the height of the left subtree of 60 by one layer, and increase the height of the right subtree by one layer, that is, raise the left subtree up, so that 60 will turn down, because 60 is larger than 30, so you can only put it In the right subtree of 30, and if 30 has a right subtree, the value of the root of the right subtree must be greater than 30 and less than 60. It can only be placed in the left subtree of 60. After the rotation is completed, update the balance factor of the node. can. During the rotation process, there are several situations to consider:

  1. The right child of the 30 node may or may not exist
  2. 60 may be a root node or a subtree.
    If it is a root node, the root node must be updated after the rotation is completed;
    if it is a subtree, it may be the left subtree or the right subtree of a node.
void RotateRight(Node* parent)
{
    
    
    // subL: parent的左孩子
    // subLR: parent左孩子的右孩子
    Node* subL = parent->left;
	Node* subLR = subL->right;
    // 旋转完成之后,30的右孩子作为双亲的左孩子
    parent->left = subLR;
    // 如果30的左孩子的右孩子存在,更新双亲
    if (subLR)
		subLR->parent = parent;
    
    // 60作为30的右孩子
    subL->right = parent;

    // 因为60可能是棵子树,因此在更新其双亲前必须先保存60的双亲
    Node* pparent = parent->parent;

    // 更新60的双亲
    parent->parent = subL;

    // 更新30的双亲
    subL->parent = pparent;
    // 如果60是根节点,根新指向根节点的指针
    if (nullptr == pparent)
			_root = subL;
    else
    {
    
    
    // 如果60是子树,可能是其双亲的左子树,也可能是右子树
        if (parent == pparent->left)
			pparent->left = subL;
		else
			pparent->right = subL;
    }
    // 根据调整后的结构更新部分节点的平衡因子
    parent->bf = subL->bf = 0;
}

4.2 Left single spin

The new node is inserted into the right side of the higher right subtree.
Insert picture description here
Refer to right single rotation for the situation.

void RotateLeft(Node* parent)
{
    
    
	Node* subR = parent->right;
	Node* subRL = subR->left;

	parent->right = subRL;
	if (subRL)
		subRL->parent = parent;

	subR->left = parent;

	Node* pparent = parent->parent;
	subR->parent = pparent;
	parent->parent = subR;

	if (nullptr == pparent)
		_root = subR;
	else
	{
    
    
		if (parent == pparent->left)
			pparent->left = subR;
		else
			pparent->right = subR;
	}

	parent->bf = subR->bf = 0;
}

4.3 Single spin left and then right

The new node is inserted to the right of the higher left subtree.
Insert picture description here
Turn the double spin into a single spin and then rotate, that is: first single-rotate 30 to the left, then single-rotate 90 to the right, and then consider the update of the balance factor after the rotation is completed.

// 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整
void RotateLR(Node* parent)
{
    
    
	Node* subL = parent->left;
	Node* subLR = subL->right;
	// 旋转之前,保存subLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
	int bf = subLR->bf;
	
	// 先对30进行左单旋
	RotateLeft(parent->left);
	// 再对90进行右单旋
	RotateRight(parent);

	if (1 == bf)
		subL->bf = -1;
	else if (-1 == bf)
		parent->bf = 1;
}

4.4 Single right spin first and then left single spin

The new node is inserted to the left of the higher right subtree.
Insert picture description here
Refer to the situation for single left rotation and then right single rotation.

void RotateRL(Node* parent)
{
    
    
	RotateRight(parent->right);
	RotateLeft(parent);
}

5. AVL tree verification

The AVL tree adds a balance restriction on the basis of the binary search tree. Therefore, to verify the AVL tree, there are two steps:

  1. Verify that it is a binary search tree
    If an ordered sequence can be obtained by traversing in the middle order, it is a binary search tree.
void _InOrder(Node* root)
{
    
    
	if (root)
	{
    
    
		_InOrder(root->left);
		cout << root->data << " ";
		_InOrder(root->right);
	}
}
  1. Verify that it is a balanced tree
    . The absolute value of the height difference of each node's subtree does not exceed 1 (note if there is no balance factor in the node); whether the node's balance factor is calculated correctly.
int _Height(Node* root)
{
    
    
	if (nullptr == root)
		return 0;

	int leftHeight = _Height(root->left);
	int rightHeight = _Height(root->right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

bool _IsBalanceTree(Node* root)
{
    
    
	if (nullptr == root)
		return true;
	int leftHeight = _Height(root->left);
	int rightHeight = _Height(root->right);
	if (abs(root->bf) > 1 || (rightHeight - leftHeight != root->bf))
	{
    
    
		cout << root->data << ":" << root->bf << endl;
		return false;
	}

	return _IsBalanceTree(root->left) && _IsBalanceTree(root->right);
}

6. AVL tree performance

The AVL tree is an absolutely balanced binary search tree. It requires that the absolute value of the height difference between the left and right subtrees of each node does not exceed 1, which can ensure the efficient time complexity of the query, that is, log2N. But if you want to do some structural modifications to the AVL tree, the performance is very low. For example, it needs to maintain its absolute balance when inserting, and the number of rotations is relatively large. What is worse is that when deleting, it is possible to keep rotating until the root. s position. Therefore: if you need an efficient and ordered data structure for querying, and the number of data is static (that is, it will not change), you can consider an AVL tree, but a structure that is frequently modified is not suitable.

Guess you like

Origin blog.csdn.net/zhao_leilei/article/details/113029068