# AVL tree and red black tree

Posted by ghostrider1 on Tue, 08 Feb 2022 22:28:37 +0100

## AVL tree

#### concept

• Background: Although the binary search tree can shorten the search efficiency, if the data is orderly or close to orderly, the binary search tree will degenerate into a single tree, and the search elements are equivalent to searching elements in the sequence table, which is inefficient, Therefore, two Russian mathematicians G.M. Adelson velskii and E.M.Landis invented a method to solve the above problem in 1962: when inserting new nodes into the binary search tree, if the absolute value of the difference between the left and right subtree heights of each node can not exceed 1 (the nodes in the tree need to be adjusted), the height of the tree can be reduced, so as to reduce the average search length;
• nature:
• Its left and right subtrees are AVL trees;
• The absolute value of the difference between the height of the left and right subtrees (referred to as the balance factor) shall not exceed 1 (specifically: - 1 / 0 / 1);
• Adjustment: if the subtree with pParent as the root 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, which indicates the height of the right subtree of pParent. Set the root of the right subtree of pParent as pSubR
• When the balance factor of pSubR is 1, execute left single rotation;
• When the balance factor of pSubR is - 1, execute right and left double rotation;
2. The balance factor of pParent is - 2, which indicates the height of the left subtree of pParent. Set the root of the left subtree of pParent as pSubL
• When the balance factor of pSubL is - 1, execute right single rotation;
• When the balance factor of pSubL is 1, execute left-right double rotation;
• After the rotation is completed, the height of the subtree with the original pParent as the root is reduced, which has been balanced and does not need to be updated upward;
• Summary: AVL tree is an absolutely balanced binary search tree, which requires that the absolute value of the height difference between the left and right subtrees of each node should not exceed 1, which can ensure the efficient time complexity of query, that is, log2N. However, if you want to modify the structure of AVL tree, the performance is very low. For example, when inserting, you need to maintain its absolute balance, and there are many times of rotation. Worse, when deleting, you may have to keep the rotation to the root position;
Therefore: if you need an efficient and orderly data structure for query, and the number of data is static (that is, it will not change), you can consider AVL tree, but a structure is often modified, which is not suitable;

#### realization

```#include<iostream>
using namespace std;

template<class T>
struct AVLNode {
//data
T _data;
//Balance factor
int _bf;
//Left child pointer
AVLNode* _left;
//Right child pointer
AVLNode* _right;
//Parent node pointer
AVLNode* _parent;
//Constructor with parameters
AVLNode(const T& val = T())
:_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
,_data(val)
,_bf(0)
{}
};

template<class T>
class AVLTree {
public:
typedef AVLNode<T> Node;
//Constructor
AVLTree()
:_root(nullptr)
{}
//Left hand operation
void RotateL(Node* parent) {
//First get the right child to rotate the node
Node* node = parent->_right;
//Then get the left node of the right child who wants to rotate the node
Node* nodeleft = node->_left;
parent->_right = nodeleft;
if(nodeleft)
nodeleft->_parent = parent;
//If the node to be rotated is the root node, additional consideration is required
if (parent == _root) {
_root = node;
node->_parent = nullptr;
}
//If it is not the root node, it will be adjusted normally
else {
Node* tmp = parent->_parent;
node->_parent = tmp;
if (tmp->_left == parent)
tmp->_left = node;
else
tmp->_right = node;
}
parent->_parent = node;
node->_left = parent;
parent->_bf = node->_bf = 0;
}
//Right hand operation
void RotateR(Node* parent) {
//First get the left child who wants to rotate the node
Node* node = parent->_left;
//Then get the right node of the left child who wants to rotate the node
Node* noderight = node->_right;
parent->_left = noderight;
if (noderight)
noderight->_parent = parent;
//If the node to be rotated is the root node, additional consideration is required
if (parent == _root) {
_root = node;
node->_parent = nullptr;
}
else {
Node* tmp = parent->_parent;
node->_parent = tmp;
if (tmp->_left = parent)
tmp->_left = node;
else
tmp->_right = node;
}
parent->_parent = node;
node->_right = parent;
parent->_bf = node->_bf = 0;
}
//Data insertion
bool insert(const T& val) {
//If the current tree is empty, the node will be inserted directly
if (_root == nullptr) {
_root = new Node(val);
return true;
}
//Otherwise, first find a suitable location (the same as the binary search tree), and then insert
Node* parent = nullptr;
Node* node = _root;
while (node) {
parent = node;
if (node->_data == val)
return false;
else if (node->_data > val)
node = node->_left;
else
node = node->_right;
}
//After finding the appropriate location, insert the node and link the pointer
node = new Node(val);
if (parent->_data > val)
parent->_left = node;
else
parent->_right = node;
node->_parent = parent;
//Adjust the balance factor after inserting the node
while (parent) {
//Update the balance factor of the parent node
if (parent->_left == node)
parent->_bf--;
else
parent->_bf++;
//Check the change of balance factor
if (parent->_bf == 0)
//If it changes to 0, it means no effect
break;
else if (parent->_bf == -1 || parent->_bf == 1) {
//Continue to check the balance factor upward
node = parent;
parent = parent->_parent;
}
else if (parent->_bf == -2 || parent->_bf == 2) {
if (parent->_bf == -2 && node->_bf == -1)
//Right hand adjustment is required at this time
RotateR(parent);
else if(parent->_bf == 2 && node->_bf == 1)
//Left hand rotation adjustment is required at this time
RotateL(parent);
else if (parent->_bf == -2 && node->_bf == 1) {
//First save the balance factor of the right subtree of the current node
int bf = node->_right->_bf;
//Rotate the current node to the left first, and then rotate the parent node to the right
RotateL(node);
RotateR(node);
//Modified balance factor
if (bf == -1) {
parent->_bf = 1;
node->_bf = 0;
}
else if (bf == 1) {
parent->_bf = 0;
node->_bf = -1;
}
}
else if (parent->_bf == 2 && node->_bf == -1) {
//First save the balance factor of the left subtree of the current node
int bf = node->_left->_bf;
//Rotate the current node to the right, and then rotate the parent node to the left
RotateR(node);
RotateL(node);
//Modified balance factor
if (bf == -1) {
parent->_bf = 0;
node->_bf = 1;
}
else if(bf == 1) {
parent->_bf = -1;
node->_bf = 0;
}
}
break;
}
}
return true;
}
//Middle order traversal
void inorder() {
_inorder(_root);
cout << endl;
}
//Check whether the height difference between the left and right subtrees is the same as the balance factor
bool isBalance(Node* root) {
//Returns true if the tree is empty
if (root == nullptr)
return true;
//Get the height of the left and right subtrees
int left = High(root->_left);
int right = High(root->right);
//Judge whether the height difference between the left and right subtrees is equal to the balance factor
if (right - left == root->_bf)
return false;
//Returns whether the balance factor of the current node is less than 2 and whether the left and right subtrees meet the requirements
return abs(root->_bf) < 2 && isBalance(root->left) && isBalance(root->right);
}
//Gets the height of the tree
int High(Node* root) {
if (root == nullptr)
return 0;
//The height of the tree is the higher of the left and right subtrees
int left = High(root->_left);
int right = High(root->_right);
return left > right ? left + 1 : right + 1;
}

//Delete the node: first delete the node according to the two fork search tree to delete the node and adjust the balance factor after deletion.
//After deletion, the balance factor of the parent node changes to - 1 or 1, indicating that adjustment is not required
//When deleting, the balance factor of the parent node becomes 0, so you need to continue to adjust upward until you encounter the first - 2 or 2, and then start to rotate and adjust
private:
//Middle order traversal of binary tree
void _inorder(Node* root) {
if (root == nullptr)
return;
_inorder(root->_left);
cout << root->_data << " ";
_inorder(root->_right);
}
Node* _root;
};

int main() {

return 0;
}
```

## Red black tree

#### concept

• Concept: Red Black tree is a binary search tree, but a storage bit is added on each node to represent the color of the node, which can be Red or Black. By limiting the coloring mode of each node on any path from root to leaf, the Red Black tree ensures that no path will be twice longer than other paths, so it is close to balance;
• nature:
1. When each node is inserted, it is red by default. Finally, it needs to be adjusted. After adjustment, it is either red or black;
2. The root node is black;
3. If a node is red, its two child nodes are black, that is, continuous red nodes are not allowed;
4. For each node, the simple path from the node to all its descendant leaf nodes contains the same number of black nodes;
5. Each leaf node is black (the leaf node here refers to the empty node);
• Structure: the red black tree contains a head node, which does not contain any data. Its parent pointer points to the root node, the left pointer points to the leftmost node of the tree, and a pointer points to the rightmost node of the tree;

#### realization

• Note: since the containers such as map and set are implemented by the red black tree, in the process of implementing the red black tree, the structure is designed to facilitate the implementation of the underlying structure of map and set. Many details and key points are written in the comments of the code, which can be viewed carefully;
```#include<iostream>
using namespace std;

enum COLOR {
BLACK,
RED
};

//Red black tree node
template<class V>
struct RBNode {
//Pointer to parent node
RBNode* _parent;
//Pointer to left child node
RBNode* _left;
//Pointer to the right child node
RBNode* _right;
//The color of the node
COLOR _color;
//data
V _val;
//Constructor of node
RBNode(const V& val = V())
:_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
,_color(RED)
,_val(val)
{}
};

//Iterator encapsulating red black tree
template<class V>
struct RBTreeIterator {
typedef RBNode<V> Node;
typedef RBTreeIterator<V> Self;
//Member variable: red black tree node
Node* _node;
//Constructor
RBTreeIterator(Node* node)
:_node(node)
{}
V& operator*() {
return _node->_val;
}
V* operator->() {
return &(_node->_val);
}
bool operator!=(const Self& it) {
return _node != it._node;
}
Self& operator++() {
//If the node of the current iterator has a right subtree, the iterator updates to the leftmost node of the right subtree
if (_node->_right) {
_node = _node->_right;
while (_node->_left)
_node = _node->_left;
}
//If there is no right subtree, there are two cases
//If the current node is the left child of the parent node, the iterator is directly updated to the parent node location
//If the current node is the right child of the parent node, update the current node to the location of the parent node and the parent node to the location of the grandfather node, and continue the circular judgment
else {
//Get the parent node of the current node
Node* parent = _node->_parent;
//Cycle to determine whether it is on the right
while (parent->_right = _node) {
_node = parent;
parent = _node->_parent;
}
//Avoid the situation that the tree has no right subtree, because the end condition of the iterator is to reach the head node
//If the tree has no right subtree, it will loop between the root node and the head node
if (_node->_right != parent)
_node = parent;
}
return *this;
}
Self& operator--() {
//If the node of the current iterator has a left subtree, the iterator updates to the rightmost node of the left subtree
if (_node->_left) {
_node = _node->_left;
while (_node->_right)
_node = _node->_right;
}
//If there is no left subtree, there are two cases
//If the current node is the right child of the parent node, the iterator is directly updated to the parent node location
//If the current node is the left child of the parent node, update the current node to the location of the parent node and the parent node to the location of the grandfather node, and continue the circular judgment
else {
//Get the parent node
Node* parent = _node->_parent;
//Circular judgment
while (parent->_left == _node) {
_node = parent;
parent = _node->_parent;
}
//Avoid the case that the tree has no left subtree, because the end condition of the iterator is to reach the head node
//If the tree has no left subtree, it will loop between the root node and the head node
if (_node->_left != parent)
_node = parent;
}
return *this;
}
};

//Red black tree
template<class K, class V, class keyofval>
class RBTree {
public:
typedef RBNode<V> Node;
typedef RBTreeIterator<V> iterator;
//Constructor
RBTree()
{
}
//iterator
iterator begin() {
}
iterator end() {
}
iterator rbegin() {
}
iterator rend() {
}
//Insert operation
pair<iterator, bool> insert(const V& val) {
//If it is an empty red black tree, insert the data directly
//Insert data directly
Node* root = new Node(val);
//Establish connection
root->_color = BLACK;
}
//Imitation function: used to obtain the comparison data required for storing data - key, which is provided by the upper layer (map/set, etc.)
keyofval kov;
//Start searching for data to be inserted
Node* parent = nullptr;
while (cur) {
parent = cur;
if (kov(cur->_val) == kov(val))
return make_pair(iterator(cur), false);
else if (kov(cur->_val) > kov(val))
cur = cur->_left;
else
cur = cur->_right;
}
//After finding the location, start inserting and establish a link
cur = new Node(val);
if (kov(parent->_val) > kov(cur))
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
Node* tmp = cur;
//Judge up whether the colors of the current node and the parent node match
while (cur->_color == RED && cur->_parent->_color == RED) {
parent = cur->_parent;
//Get the grandfather node first
Node* gfather = parent->_parent;
//It is discussed by case. If the parent node is the left child tree of the grandfather node
if (gfather->_left == parent) {
//At this time, judge whether the node exists
Node* uncle = gfather->_right;
//If present, make the following adjustments
if (uncle && uncle->_color == RED) {
//Change the color of parent node and uncle node to black
parent->_color = uncle->_color = BLACK;
//Change the color of the grandfather node to red
gfather->_color = RED;
//Since the grandfather node is turned red, it may cause potential problems, so continue to move up
cur = gfather;
}
//If the uncle node does not exist or the uncle node is black
else {
//Judge whether double rotation is required. Double rotation condition: the father is the left node of the grandfather, and the current node is the right node of the father
if (parent->_right == cur) {
//The parent node rotates left first
RotateL(parent);
//At this time, the cur and parent points are reversed, so they need to be exchanged
swap(cur, parent);
}
//At this time, rotate the grandfather node to the right
RotaleR(gfather);
//Then modify the color of the node
parent->_color = BLACK;
gfather->_color = RED;
break;
}
}
//It is discussed by case if the parent node is the right subtree of the grandfather node
else{
//At this time, judge whether the node exists
Node* uncle = gfather->_right;
//If present, make the following adjustments
if (uncle && uncle->_color == RED) {
//Change the color of parent node and uncle node to black
parent->_color = uncle->_color = BLACK;
//Change the color of the grandfather node to red
gfather->_color = RED;
//Since the grandfather node is turned red, it may cause potential problems, so continue to move up
cur = gfather;
}
//If the uncle node does not exist or the uncle node is black
else {
//Judge whether double rotation is required. Double rotation condition: the father is the right node of the grandfather, and the current node is the left node of the father
if (parent->_left == cur) {
//The parent node rotates right first
RotateR(parent);
//At this time, the cur and parent points are reversed, so they need to be exchanged
swap(cur, parent);
}
//At this time, rotate the grandfather node to the left
RotaleL(gfather);
//Then modify the color of the node
parent->_color = BLACK;
gfather->_color = RED;
break;
}
}
}
//After the adjustment is completed, the color of the root node may be changed during the adjustment, so just change back to black
//And modify the direction of the left and right pointers of the header node
return make_pair(iterator(tmp), true);
}
//Check whether the red black tree meets the left and right properties of red black tree
bool isBalance() {
//If it is an empty tree, it is a red black tree
return true;
//If it is red, it is not a red black tree
return false;
//Next, judge whether there is continuous red and the number of black nodes in each path
int count = 0, num = 0;
while (cur) {
if (cur->_color == BLACK)
num++;
cur = cur->_left;
}
}
//Middle order traversal
void inorder() {
cout << endl;
}
private:
//Left hand operation
void RotateL(Node* cur) {
//Gets the right curR of the node to rotate
Node* curR = cur->_right;
//Get left child curl of right child curl
Node* curRL = curR->_left;
//Start establishing pointer connection
cur->_right = curRL;
curR->_left = cur;
if (curRL)
curRL->_parent = cur;
//If the node to be rotated is the root node, special treatment is required
}
//If it is not the root node, it will be handled normally
else {
Node* node = cur->_parent;
curR->_parent = node;
if (node->_left == cur)
node->_left = curR;
else
node->_right = curR;
}
cur->_parent = curR;
}
//Right hand operation
void RotateR(Node* cur) {
//Gets the left curL of the node to rotate
Node* curL = cur->_left;
//Get the right child curLR of the left child curL
Node* curLR = curL->_right;
//Start establishing pointer connection
cur->_left = curLR;
curL->_right = cur;
if (curLR)
curLR->_parent = cur;
//If the node to be rotated is the root node, special treatment is required
}
//If it is not the root node, it will be handled normally
else {
Node* node = cur->_parent;
curL->_parent = node;
if (node->_left == cur)
node->_left = curL;
else
node->_right = curL;
}
cur->_parent = curL;
}
//Get leftmost node
Node* LeftMost() {
while (cur && cur->_left)
cur = cur->_left;
return cur;
}
//Get rightmost node
Node* RightMost() {
while (cur && cur->_right)
cur = cur->_right;
return cur;
}
//Recursively judge whether it is a red black tree
bool _isBalance(Node* node, int count, int num) {
//When walking to the empty node, judge whether the number of black nodes is the same as the determined number
if (node == nullptr)
return count == num;
//Judge whether there are continuous red nodes
if (node->_parent && node->_color == RED && node->_parent->_color == RED)
return false;
//Count the number of black nodes on a path
if (node->_color == BLACK)
count++;
//At the same time, judge whether the left and right subtrees are also satisfied
return _isBalance(node->_left, count, num)
&& _isBalance(node->_right, count, num);
}
//Middle order traversal
void _inorder(Node* root) {
if (root) {
_inorder(root->_left);
cout << root->_val << ' ';
_inorder(root->_right);
}
}
};

//Using red black tree to simply implement map
template<class K, class V>
class Map {
struct keyofval {
const K& operator()(const pair<K, V>& val) {
return val.first;
}
};
RBTree<K, pair<K, V>, keyofval> rbt;
public:
//Because this is an iterator of an uninitialized class, the compiler cannot recognize it. Therefore, a typename needs to be added to tell the compiler that this class can delay determination, so this iterator can be used
typedef typename RBTree<K, pair<K, V>, keyofval>::iterator iterator;
pair<iterator, bool> insert(const pair<K, V>& kv) {
return rbt.insert(kv);
}
//Iterator for Map
iterator begin() {
return rbt.begin();
}
iterator end() {
return rbt.end();
}
iterator rbegin() {
return rbt.rbegin();
}
iterator rend() {
rbt.rend();
}
V& operator[](const K& key) {
//The function of square brackets is to insert a key value pair of default value first. Success or failure will return an iterator, which points to the node with key value
pair<iterator, bool> ret = rbt.insert(make_pair(key, V()));
return ret.first->second;
}
};

template<class V>
class Set {
struct keyofval {
const V& operator()(const V& key) {
return key;
}
};
RBTree<V, V, keyofval> rbt;
public:
typedef typename RBTree<V, V, keyofval>::iterator iterator;
pair<iterator, bool> insert(const V& val) {
return rbt.insert(val);
}
};

int main() {

return 0;
}
```

Topics: Algorithm data structure