# C + + small worker training manual XXVIII ------ AVL (balanced binary search tree) super detailed

Although binary search tree can improve the efficiency of search, but there is a defect, that is, when the parameters of the tree are in order or close to order, it will degenerate into a single tree, and the search efficiency will become o(n)
Therefore, a method to solve the above problem is invented: when a new node is inserted into a binary search tree, if the absolute value of the height difference between the left and right subtrees of each node is not more than 1 (the nodes in the tree need to be adjusted), the height of the tree can be reduced, thus reducing the average search length.

In order to commemorate the inventor of this solution, it is called AVL tree
An AVL tree is either an empty tree or a binary search tree with the following properties:
The premise of an AVL tree is that the AVL tree must be a binary search tree, and the binary search tree may not be an AVL tree

• Its left and right subtrees are AVL trees
• The absolute value of the difference between the height of left and right subtrees (balance factor for short) shall not exceed 1 (- 1 / 0 / 1)

First, how to judge the balance factor of a tree?
I choose the height of the right subtracted from the height of the left subtracted.

```//Because the balance factor of AVL tree itself has only three possibilities: 0, - 1, 1
//Before inserting a new node
//0 ----- pParent is the leaf node = =============, the inserted node may be inserted into the left subtree and the right subtree, so the parent node may become + - 1 after insertion
//1 ----- pParent only has right child = ========, inserted node can only insert left subtree -] 0
//-1 ----- pParent only has left child = =========, inserted node can only insert right subtree -] 0
//It means that in the last two cases, it only affects the balance factor of parents, but not the balance factor of parents' upward node
```

When the balance factor is 1 / - 1 before insertion, it becomes 0 after insertion

Then, the equilibrium factor is 0 before insertion:

First of all, it introduces what is left single selection and what is right single rotation.
Left and right turns of trees

Right single rotation scene:
Right single rotation: the newly inserted node is inserted to the left (outside) of the higher subtree (the higher subtree is the left subtree)

There are three special nodes in the process of rotation:
Three special nodes, one of which may be empty

If you mention the root of the higher subtree, that is to say, which node is unbalanced, you should mention the higher one of its left and right children

In the other case, right single rotation:

Third right single rotation: (left single branch)
But even if this node is empty, it doesn't affect anything. It just needs to list one by one the three situations of right single rotation

The above three scenes are the three scenes of right single rotation

Although the scenarios are different, the code given in each scenario is indeed the same. The three scenarios are given just to make people see them more clearly

Of course, you need to update the parent pointer fields of three special nodes after you finish the above operations

```In summary, there are three steps
1. Change the left and right child pointer fields of special nodes
2. Update the balance factor of special nodes
3. Update the pointer domain of the parent node of the special node
4. Judge whether the highest vertex is the root node, then complete the connection and update the balance factor
```

To facilitate annotation, mark from top to bottom:

pParent: special node 1
pSubl: special node 2
pSublr: special node 3

Right single rotation code:

```//Right single rotation code
void RotateRight(Node* pParent_1)  //Special node 1
{
Node* pSubL = pParent_1->_pLeft;  //Special node 2
Node* pSubLR = pSubL->_pRight;  //Special node 3
pParent->_pLeft = pSubLR;
//The main purpose of void determination here is to avoid the third case, left single branch
if (pSubLR)  //If special node 3 is not empty, let the parent pointer field of special node 3 point to special node 1
pSubLR->_pParent = pParent_1;
pSubL->_pRight = pParent_1;
//Next, we deal with the pparent ﹣ 1 node in the special node
//We don't know whether it is a root node or not, because the part of pParent we draw is a root node, but we can't guarantee that there are no other nodes above
//So we need to discuss it according to the situation
//If pParent_1 is a subtree, there are trees on the top of pParent
//In order to prevent the loss of the parents node of pParent_1, we first save the parents of pParent
Node* pPParent = pParent_1->_pParent;   //The parent node of special node 1 may be empty
pParent_1->_pParent = pSubL;  //Reset parents
pSubL->_pParent = pPParent;

if (pPParent == nullptr)  //It indicates that special node 1 has no parents,
{
_pRoot = pSubL;  //Without parents, the root node in the class is directly updated to special node 2
}
else //Note that special node 1 has parents
{
if (pPParent->_pLeft == pParent_1)  //At this time, although the parents of special node 1 are updated, the original parents of special node 1 still regard special node 1 as their own child
//So if special node 1 is the left child of the previous parents, you need to make his left child point to special node 2 now, and vice versa
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}

//And then there's the update balance factor
//After the option is updated, the balance factors of special node 1 and special node 2 become 0
//The equilibrium factor of parents of special node 1 is invariant
pParent_1->_bf = pSubL->_bf = 0;
}
```

Left single rotation code:

Select one from the left radio:

According to the right single rotation, the left single rotation can be written:

```	//Left spin
void RotateLeft(Node* pParent_1)   //Special node 1
{
Node* pSubR = pParent_1->_pRight;  //Special node 2
Node* pSubRL = pSubR->_pLeft;   //Special node 3
pParent_1->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = pParent_1;
pSubR->_pLeft = pParent_1;
Node* pPParent = pParent_1->_pParent;
pSubR->_pParent = pPParent;
pParent_1->_pParent = pSubR;
if (pPParent == nullptr)
{
_proot = pSubR;
}
else
{
if (pParent_1 == pPParent->_pLeft)
{
pPParent->_pLeft = pSubR;
}
else
{
pPParent->_pRight = pSubR;
}
}

pParent_1->_bf = pSubR->_bf = 0;
}
```

Left single rotation right single rotation:

In one case:
First, the left subtree of the unbalanced node is rotated to the left

Right single rotation of the whole

Left single rotation right single rotation code:
This is not only the reuse of left single rotation and right single rotation, because there are some special cases. When left single rotation and right single rotation, we directly make the balance factor of special node 1 and special node 2 equal to 0 at the end, but there are some specific methods for some specific AVL trees when rotating, so we need to judge some cases.

```	//Left single turn right single turn
void RotateLR(Node* pParent)
{
//Save node before rotation
Node* pSubR = pParent->_pLeft;
Node* pSubRL = pSubR->_pRight;
int bf = pSubRL->_bf;

RotateLeft(pParent->_pLeft);
RotateRight(pParent);

//Three situations
//BF = = 1 | - 1 | - 0,0 has been dealt with internally
if (bf == 1)
pSubRL->_bf = -1;
else if (bf == -1)
pParent->_bf = 1;
}
```

Right single rotation left single rotation:
The same is true for right single spin and left single spin
Right single rotation left single rotation code:

```//Right single spin left single spin
void RotateRL(Node* pParent)
{
//It is found that the equilibrium factors of some nodes need to be updated during the right left double rotation, which is also a special case
//Here, we will not draw a picture for analysis, but directly give the code
Node* pSubR = pParent->_pRight;
Node* pSubRL = pSubR->_pLeft;
int bf = pSubRL->_bf;

RotateRight(pParent->_pRight);
RotateLeft(pParent);

//Three situations
//BF = = 1 | - 1 | - 0,0 has been dealt with internally
if (bf == 1)
pParent->_bf = -1;
else if (bf == -1)
pSubR->_bf = 1;
}
```

Final code:

```#include<iostream>
using namespace std;
template<class T>  //Node structure
struct AVLTreeNode
{
AVLTreeNode(const T& data)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _bf(0)
{}
AVLTreeNode<T>* _pLeft; // The left child of this node
AVLTreeNode<T>* _pRight; // The right child of this node
AVLTreeNode<T>* _pParent; // Parents of the node
T _data;
int _bf; // Balance factor of this node
};

//In the following insert operation, to determine the balance factor of a node, we default to use the right subtree - the left subtree
//Again, the default is right subtree - left subtree
template<class T>
class AVLTree
{
typedef AVLTreeNode<T> Node;
public:
AVLTree()
:_pRoot(nullptr)
{}
bool Insert(const T& data)
{
//AVL tree may be empty
if (nullptr == _pRoot)
{
_pRoot = new Node(data);
return true;
}
//Not empty
//First, insert the binary search tree
//The first thing to say is that the insertion position is absolutely that the leaf node has only the left child's node and the right child's node
//It will never be inserted at nodes with both left and right children
Node* pCur = _pRoot;
Node* pParent = nullptr;
while (pCur)
{
pParent = pCur;   //Save the parent node of the node to be inserted
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
return false;  //This means that if the value tree to be inserted already exists, it will be returned directly. The values inserted in the default tree are different
}
//Insert new node
pCur = new Node(data);  //Create node first
if (data > pParent->_data)
pParent->_pRight = pCur;
else
pParent->_pLeft = pCur;
pCur->_pParent = pParent; //Finally, let the parents of the new node point to pParent

//Every time a new node is inserted, the balance factor of the parent node must change

//Renew the balance factor of parents
while (pParent)   //Circular condition is that parents are not empty
{
if (pCur == pParent->_pLeft)  //If it is a left subtree, then the balance factor of parents --. Because my default balance factor algorithm is to subtract the left subtree from the right subtree
pParent->_bf--;
else
pParent->_bf++;

//Because the balance factor of AVL tree itself has only three possibilities: 0, - 1, 1
//Before inserting a new node
//0 ----- pParent is the leaf node = =============, the inserted node may be inserted into the left subtree and the right subtree, so the parent node may become + - 1 after insertion
//1 ----- pParent only has right child = ========, inserted node can only insert left subtree -] 0
//-1 ----- pParent only has left child = =========, inserted node can only insert right subtree -] 0
//It means that in the last two cases, it only affects the balance factor of parents, but not the balance factor of parents' upward node
if (pParent->_bf == 0)  //That is to say, after the above + + || -- is finished, the balance factor of the inserted element's node becomes 0, which is the last two situations I mentioned above. Because it does not affect the other nodes, it can be returned directly
return true;
else if (pParent->_bf == -1 || pParent->_bf == 1)  //This is the case of the parent node that will affect the insertion node, and then all the nodes up
{
//Adjust the position of pCur here
pCur = pParent;   //At this point, you need to enter the while loop to update the + + | -- at the beginning
pParent = pCur->_pParent;
}
else
{
//Balance factor of parent = = 2 | - 2
//It means that the nature of AVL tree has been violated at this time -- at this time, the tree is unbalanced, and the tree with pParent as root should be rotated
//There are four kinds of rotation
//Left spin
//Right single rotation
//If the equilibrium factor is + 2, because we take the left subtree from the right subtree, so this is the left single rotation, otherwise it is the right single rotation
//Of course, it is not enough to judge only the unbalanced nodes, because there are left-right double rotation and right-left double rotation, so it is necessary to judge the left and right subtrees of the unbalanced nodes

//Left and right double rotation: left radio, right single rotation
//Right left double rotation: left radio, right single rotation
if (pParent->_bf == 2)
{
//The right subtree height of pParent
//The equilibrium factor of pCur is + 1
//Since only one path of the inserted node will be modified after insertion, pCur is the child node of pParent, so you don't need to know whether it is the left or right child, because you don't need to know
if (pCur->_bf == 1)
{
//Left spin
RotateLeft(pParent);
}
else
{
//The equilibrium factor of pCur is - 1
//Right left double spin
RotateRL(pParent);
}
}
else
{
//Left subtree height of pParent
//pParent->_bf == -2
if (pCur->_bf == -1)
{
//Right single rotation
RotateRight(pParent);
}
else
{
//Left and right rotation
RotateLR(pParent);
}
}
break;   //When the rotation is completed, you don't need to enter the while again. You can exit directly because the number of layers before and after the imbalance caused by the insertion of nodes does not change, which has no impact on the above
}
}
return true;
}
//In order to avoid confusion with the pParent in the node structure, I use pParent_1 instead of the special node 1
void RotateRight(Node* pParent_1)  //Special node 1
{
Node* pSubL = pParent_1->_pLeft;  //Special node 2
Node* pSubLR = pSubL->_pRight;  //Special node 3
pParent_1->_pLeft = pSubLR;
//The main purpose of void determination here is to avoid the third case, left single branch
if (pSubLR)  //If special node 3 is not empty, let the parent pointer field of special node 3 point to special node 1
pSubLR->_pParent = pParent_1;
pSubL->_pRight = pParent_1;
//Next, we deal with the pparent ﹣ 1 node in the special node
//We don't know whether it is a root node or not, because the part of pParent we draw is a root node, but we can't guarantee that there are no other nodes above
//So we need to discuss it according to the situation
//If pParent_1 is a subtree, there are trees on the top of pParent
//In order to prevent the loss of the parents node of pParent_1, we first save the parents of pParent
Node* pPParent = pParent_1->_pParent;   //The parent node of special node 1 may be empty
pParent_1->_pParent = pSubL;  //Reset parents
pSubL->_pParent = pPParent;

if (pPParent == nullptr)  //It indicates that special node 1 has no parents,
{
_pRoot = pSubL;  //Without parents, the root node in the class is directly updated to special node 2
}
else //Note that special node 1 has parents
{
if (pPParent->_pLeft == pParent_1)  //At this time, although the parents of special node 1 are updated, the original parents of special node 1 still regard special node 1 as their own child
//So if special node 1 is the left child of the previous parents, you need to make his left child point to special node 2 now, and vice versa
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}

//And then there's the update balance factor
//After the option is updated, the balance factors of special node 1 and special node 2 become 0
//The equilibrium factor of parents of special node 1 is invariant
pParent_1->_bf = pSubL->_bf = 0;
}

//Left spin
void RotateLeft(Node* pParent_1)   //Special node 1
{
Node* pSubR = pParent_1->_pRight;  //Special node 2
Node* pSubRL = pSubR->_pLeft;   //Special node 3
pParent_1->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = pParent_1;
pSubR->_pLeft = pParent_1;
Node* pPParent = pParent_1->_pParent;
pSubR->_pParent = pPParent;
pParent_1->_pParent = pSubR;
if (pPParent == nullptr)
{
_pRoot = pSubR;
}
else
{
if (pParent_1 == pPParent->_pLeft)
{
pPParent->_pLeft = pSubR;
}
else
{
pPParent->_pRight = pSubR;
}
}

pParent_1->_bf = pSubR->_bf = 0;
}

//Right single spin left single spin
void RotateRL(Node* pParent)
{
//It is found that the equilibrium factors of some nodes need to be updated during the right left double rotation, which is also a special case
//Here, we will not draw a picture for analysis, but directly give the code
Node* pSubR = pParent->_pRight;
Node* pSubRL = pSubR->_pLeft;
int bf = pSubRL->_bf;

RotateRight(pParent->_pRight);
RotateLeft(pParent);

//Three situations
//BF = = 1 | - 1 | - 0,0 has been dealt with internally
if (bf == 1)
pParent->_bf = -1;
else if (bf == -1)
pSubR->_bf = 1;
}
//Left single turn right single turn
void RotateLR(Node* pParent)
{
Node* pSubR = pParent->_pLeft;
Node* pSubRL = pSubR->_pRight;
int bf = pSubRL->_bf;

RotateLeft(pParent->_pLeft);
RotateRight(pParent);

//Three situations
//BF = = 1 | - 1 | - 0,0 has been dealt with internally
if (bf == 1)
pSubRL->_bf = -1;
else if (bf == -1)
pParent->_bf = 1;
}
void InOreder()   //Sequential traversal
{
_InOrder(_pRoot);
}
bool Tree_Hight()  //Judge whether this tree is an AVL tree
{
return _Tree_Hight(_pRoot);
}
private:
void _InOrder(Node* pRoot)  //Sequential traversal
{
if (pRoot == nullptr)
return;
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << endl;
_InOrder(pRoot->_pRight);
}

bool _Tree_Hight(Node* pRoot)  //Judge whether AVL tree
{
if (pRoot == nullptr)
return true;
int Left_Hight = Hight(pRoot->_pLeft);
int Right_Hight = Hight(pRoot->_pRight);
int bf = Right_Hight - Left_Hight;  //Self calculated balance factor
if (abs(bf) > 1 || bf != pRoot->_bf)
return false;
return _Tree_Hight(pRoot->_pLeft) && _Tree_Hight(pRoot->_pRight);
}

int  Hight(Node* pRoot)
{
if (pRoot == nullptr)
return 0;
int L = Hight(pRoot->_pLeft);
int R = Hight(pRoot->_pRight);
return L > R ? L + 1 : R + 1;
}

Node* _pRoot;
};

void test()
{
int array[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree<int> t;
for (auto e : array)
t.Insert(e);
t.InOreder();
cout << t.Tree_Hight() << endl;
}
int main()
{
test();
return 0;
}

```
Published 230 original articles, won praise 28, visited 9266

Posted by suavebum on Sun, 12 Jan 2020 00:11:03 -0800