Height balanced binary search tree (AVL tree)

Table of contents

foreword

1. AVL tree - height balanced binary search tree

1.1 The concept of AVL tree

1.2 Definition of AVL tree node - triple chain

2. AVL tree insertion

2.1 Rotation of AVL tree

        2.1.1 A new node is inserted into the right side of the higher right subtree --- right right: left single rotation

        2.1.2 A new node is inserted to the left side of the higher left subtree --- left left: right single rotation (logic same as left single rotation)

        2.1.3 The new node is inserted into the right side of the higher left subtree --- left and right: first left single rotation and then right single rotation

        2.1.4 A new node is inserted into the left side of the higher right subtree --- right left: first right single rotation and then left single rotation

 Summarize:


Major events in the world must be done in detail!

foreword

        AVLtree is a binary search tree with "additional balance conditions". The establishment of its balance condition is to ensure that the depth of the whole tree is O(logN). Intuitively, the optimal balance condition is that the left and right subtrees of each node have the same height, but this is too strict, and it is difficult for us to insert new elements while maintaining such a balance condition. AVLtree then takes the next best thing, requiring that the height difference between the left and right subtrees of any node is at most 1. This is a weaker condition, but still guarantees a "log-depth" equilibrium state.

1. AVL tree - height balanced binary search tree

1.1 The concept of AVL tree

         Although the binary search tree can shorten the search efficiency, if the data is ordered or close to the order, the binary search tree will degenerate into a single branch tree, and searching for elements is equivalent to searching for elements in the sequence table, which is inefficient .
Therefore, two Russian mathematicians GMAdelson-Velskii and EM Landis invented a method to solve the above problem in 1962: when a new node is inserted into the binary search tree, if the left and right subtrees of each node can be guaranteed If the absolute value of the height difference does not exceed 1 (the nodes in the tree need to be adjusted) , the height of the tree can be reduced, thereby reducing the average search length.

Balance factor: right subtree height - left subtree height

An AVL tree is either an empty tree or a binary search tree with the following properties:
  • Its left and right subtrees are both AVL trees
  • The absolute value of the difference between the heights of the left and right subtrees (referred to as the balance factor) does not exceed 1 (-1/0/1)
If a binary search tree is height balanced, it is an AVL tree. If it has n nodes, its height can be kept at
O(log n), search time complexity O(log n).

1.2 Definition of AVL tree node - triple chain

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K,V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	int _bf;

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

2. AVL tree insertion

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. So
The insertion process of the AVL tree can be divided into two steps:
  • Insert new nodes in the way of binary search tree
  • Adjust the balance factor of the node

Insertion affects his ancestors (parent path), the height of the left subtree has changed, parent->_bf--; the height of the right subtree has changed, parent->_bf++

The front of the insert operation is the same as the binary search tree, with an additional cur->_parent pointing to the parent operation.

Re-control balance: update balance factor.

Rules for updating balance factors:

1. Newly added on the right, parent->_bf++; newly added on the left, parent->_bf--

2. After the update, parent->_bf == 1 or -1 , indicating that the balance factor of the parent before insertion is 0, indicating that the heights of the left and right subtrees are equal, and now one side is taller after insertion, and the height of the parent has changed, so it needs to continue to be updated

3.  After the update, parent->_bf == 0 , indicating that the balance factor of the parent before insertion is 1 or -1, indicating that one side of the left and right subtrees is higher and the other is lower. Now after insertion, both sides are the same height, and the insertion fills in the short side , the height of the subtree where the parent is located remains unchanged, and there is no need to continue to update

4.  After the update, parent->_bf == 2 or -2 , indicating that the balance factor of the parent before insertion is 1 or -1, which has already balanced the critical value. After insertion, it becomes 2 or -2, which breaks the balance and the child where the parent is located Trees need to be rotated .

 Here are a few examples (all without rotation): the worst case to the root node (ie parent==nullptr)

Code:

        //控制平衡因子
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}
			//插入后为0,说明插入前为1/-1,说明高度不一致,但是插入必定向矮的那端插入,插入后高度一致
			if (parent->_bf == 0)
			{
				break;
			}
			//插入后为1/-1,说明插入前为0,说明高度一致,现在高度改变,向上更新
			else if (abs(parent->_bf) == 1)
			{
				parent = parent->_parent;
				cur = cur->_parent;
			}
			//插入后为2,打破平衡,需要旋转处理
			else if (abs(parent->_bf == 2))
			{
				if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
	}

2.1 Rotation of AVL tree

AVL tree balance condition: the difference between the left and right subtree heights of any node is at most 1

When parnt->_bf==2 or -2 after updating, the balance critical value is broken, and a rotation operation is required at this time.

Rotation principle:

        a. Rotate into a balanced tree b. Keep search tree rules

There are 4 types of rotation:

        1. Left single rotation

        2. Right single rotation

        3. Left and right double rotation

        4. Right-left birotation

Among them (1, 2) are symmetrical to each other, called the outside (outside) insertion. (3, 4) are symmetrical to each other and are called inside insertions.

        2.1.1 A new node is inserted into the right side of the higher right subtree --- right right: left single rotation

Abstract graph:

In the above figure, before insertion, the AVL tree is balanced, and the new node is inserted into the right subtree of 60 ( note: this is not the right child ), and a layer is added to the right subtree of 60, resulting in an unbalanced binary tree rooted at 30 , to make 30 balanced, only the height of the right subtree of 30 can be reduced by one level, and the height of the left subtree can be increased by one level.

  The right subtree is about to be lifted up, so that 30 is turned down, because 30 is smaller than 6 0 , it can only be placed in the left subtree of 6 0 , and if 6 0 has a left subtree, the value of the root of the left subtree must be greater than 30 , less than 60 , it can only be placed in the right subtree of 30 , after the rotation is completed, just update the balance factor of the node. During rotation, there are several situations to consider:
  1. The left child of node 60 may or may not exist
  2. 30 may be the root node or a subtree
    If it is the root node, after the rotation is complete, the root node should be updated
    If it is a subtree, it may be the left subtree of a node, or it may be the right subtree

Figurative image:

Code:

void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		//根左旋,压下去,连接subRL
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		//记录下parent的父节点
		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		//如果根结点是父节点
		if (_root == parent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}

			subR->_parent = ppNode;
		}
		subR->_bf = parent->_bf = 0;

	}

        2.1.2 A new node is inserted to the left side of the higher left subtree --- left left: right single rotation (logic same as left single rotation)

In the above figure, 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 adds a layer, resulting in an unbalanced binary tree rooted at 60. , to make 60 balance, we can only reduce the height of the left subtree of 60 by one level, and increase the height of the right subtree by one level, that is, lift the left subtree up, so that 60 can be rotated down, because 60 is larger than 30 , we can only put it down In the right subtree of 30 , if 30 has a right subtree, the value of the root of the right subtree must be greater than 30 and less than 60 , and it can only be placed in the left subtree of 60. After the rotation is completed, the balance factor of the update node is Can. During rotation, there are several situations to consider:

1. The right child of node 30 may or may not exist
2. 60 may be the root node or a subtree
    If it is the root node, after the rotation is complete, the root node should be updated
    If it is a subtree, it may be the left subtree of a node, or it may be the right subtree

example:

        In the lateral insertion state, the only case of K2 "balanced before insertion, unbalanced after insertion" is shown on the left side of the figure. The A subtree grows one level, making it 2 deeper than the C subtree. It is impossible for the B subtree to be at the same level as the A subtree, otherwise k2 would be in an unbalanced state before insertion. It is also impossible for the B subtree to be at the same level as the C subtree, otherwise the first violation of the balance condition will be k1 instead of k2.

         In order to adjust the balance state, we hope to raise the A subtree by one level and drop the C subtree by one level—this is already a step further than the balance condition required by the AVL-tree. The right side of the figure is the adjusted situation. We can imagine this way, lift k1 up, make k2 slide down naturally, and hang the B subtree to the left of k2. This is done because the rules of the binary search tree let us know that k2>k1, so k2 must become the right child of k1 in the new tree. The rules of the binary search tree also tell us that the key values ​​of all nodes of the B subtree are between k1 and k2, so the B subtree in the new tree shape must fall to the left of k2.

 Code:

void RotateR(Node* parent)
	{
		Node* subL = parent->_right;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;
		
		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}
		subL->_bf = parent->_bf = 0;
	}

        2.1.3 The new node is inserted into the right side of the higher left subtree --- left and right: first left single rotation and then right single rotation

Turn the double rotation into a single rotation and then rotate, that is: first perform a left single rotation on 30, and then perform a right single rotation on 90, and then consider updating the balance factor after the rotation is completed (the balance factor is not fixed, because it may be in b Insert, it may also be inserted at c, or it may be h=0, inserted at the right of 30). The balance factor of 60 before the rotation is the basis for judgment, and the judgment is recorded.

Figurative image:

 Code:

void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == 1)
		{
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 0)
		{
			subL->_bf = parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

        2.1.4  The new node is inserted into the left side of the higher right subtree --- right left: first right single rotation and then left single rotation

 Summarize:

If the subtree rooted at pParent is unbalanced, that is, the balance factor of pParent is 2 or -2, consider the following situations
1. The balance factor of pParent is 2, indicating the height of the right subtree of pParent, and the root of the right subtree of pParent is pSubR
        Left single rotation is performed when the balance factor of pSubR is 1
        Right-left double rotation is performed when the balance factor of pSubR is -1
2. The balance factor of pParent is -2, indicating the height of the left subtree of pParent, and the root of the left subtree of pParent is pSubL
        When the balance factor of pSubL is -1, perform right single rotation
        When pSubL has a balance factor of 1, left and right double rotation is performed

After the rotation is completed, the height of the subtree rooted at the original pParent is reduced, and it has been balanced, so no upward update is required.

The value and significance of rotation:

1. balance

2. Reduce the height (restore to the appearance before insertion)

Guess you like

Origin blog.csdn.net/bang___bang_/article/details/131793941