Data Structure:Tree: unordered tree,Binary Search Tree,AVL,B Tree,Red Black Tree

Article directory

tree

All code address of this article: https://github.com/LittleWhiteMouse/Tree

Basic concepts

Introduction reason: tree can take into account the search and insertion characteristics of array and linked list.

Degree: a node has several children

Leaves: nodes without children

Degree of tree: the maximum degree of all nodes

Number of layers: root on the first layer

Depth: total number of nodes from root to a node

Height: the total number of nodes from the current node to the farthest leaf node

Depth of a node + height of the node = height of the tree

Ordered tree: a tree whose order is determined by numbering the same parent's child nodes

Unordered tree: there is no sequential relationship between the child nodes of any node of tree species

Connected graph: there are paths between nodes, the tree is acyclic connected graph!

All trees can be represented by binary trees, as long as the structure of the elder brother representation is rotated 45 °

Properties of binary tree

  • For any non empty binary tree, if the number of leaf nodes is n0 and the number of degree 2 nodes is n2, then n0 = n2+1
    • Derivation process: assuming that the number of nodes with degree 1 is n1, the number of summary points of the binary tree is n = n0+n1+n2, and the number of edges of the binary tree is T = n1+2*n2 = n-1 = n0+n1+n2-1, moving left and right to get the conclusion.

Binary tree classification

  • True binary tree: the degree of all nodes is either 0 or 2, that is, there is no node with only one child node
  • Full binary tree: the degree of all nodes is either 0 or 2, and all leaf nodes are in the last layer.
  • Complete binary tree: it is filled in layers, and the last layer is filled from left to right. Suppose that the height of the tree is h, and the sum of points of the tree is n, h = floor(log2(n))
    • Example: it is known that a complete binary tree has 768 nodes. Find the number of leaf nodes. Answer: set the number of leaf nodes as N0, the number of nodes with degree 1 as n1, and the number of nodes with degree 2 as n2. The number of summary points is n = n0+n1+n2, and n0=n2+1, so n = n1+2*n0-1. For a complete binary tree, n1 is 0 or 1.n is even, so n1 is 1, n0=768/2=384.

Construction and traversal of unordered tree

BinTree.h

#pragma once
#include <queue>

template <class T>
class BinNode {
public:
	T data;
	BinNode<T> *left, *right;
	BinNode() { left = right = nullptr; }
	BinNode(const T&e, BinNode<T>*l = nullptr, BinNode<T>*r = nullptr) {
		data = e; left = l; right = r;
	}
};

template <class T>
class BinTree
{
protected:
	BinNode<T> *root;
	
public:
	BinTree() { root = nullptr; }
	BinTree(BinNode<T> *r) { root = r; }
	~BinTree() {}

	void visit(BinNode<T> *node) { std::cout << node->data << " "; }

	//Preorder traversal
	void preOrder() { preOrder(root); }
	void preOrder(BinNode<T>*curnode) {
		if (curnode)
		{
			visit(curnode);
			preOrder(curnode->left);
			preOrder(curnode->right);
		}
	}

	//Sequential traversal
	void midOrder(){ midOrder(root); }
	void midOrder(BinNode<T> *curnode) {
		if (curnode)
		{
			midOrder(curnode->left);
			visit(curnode);
			midOrder(curnode->right);
		}
	}
	
	// Post order traversal
	void postOrder() { postOrder(root); }
	void postOrder(BinNode<T>*curnode) {
		if (curnode)
		{
			postOrder(curnode->left);
			postOrder(curnode->right);
			visit(curnode);
		}
	}

	// Sequence traversal: apply for a new queue and press the head node into the queue
	// Print the node value every time you leave the team. If the left child is not empty, the left child enters the team. If any child is not empty, the right child enters the team
	// Until the queue is empty
	void levelOrder() {
		std::queue<BinNode<T>*> q;
		BinNode<T> * curnode = root;
		while(curnode)
		{
			visit(curnode);
			if (curnode->left) { q.push(curnode->left); }
			if (curnode->right) { q.push(curnode->right); }
			if (q.empty()) return;
			curnode = q.front(); 
			q.pop();
		}
	}
};

Test code main.cpp

#include <iostream>
#include "BinTree.h"
using namespace std;

int main() {
	BinNode<char> A('A'), B('B'), C('C'),D('D'), E('E'),F('F'),G('G'),H('H'),I('I');
	A.left = &B; A.right = &C;
	B.left = &D;
	C.left = &E; C.right = &F;
	D.left = &G; D.right = &H;
	E.right = &I;

	BinTree<char> tree(&A);
	cout << "Preorder traversal:"; tree.preOrder(); cout << endl;
	cout << "Middle order traversal:"; tree.midOrder(); cout << endl;
	cout << "Subsequent traversal:"; tree.postOrder(); cout << endl;
	cout << "Subsequent traversal:"; tree.levelOrder(); cout << endl;
	
	system("pause");
	return 0;
}

BST binary search tree

Binary Search Tree

  • Every element has a key, and repetition is not allowed
  • The key of the left subtree is smaller than the key of the root node
  • The key of the right subtree is greater than the key of the root node
  • Left and right subtrees are binary search trees
  • Tree is binary operation log when searching and deleting data
  • The middle order traversal of binary search tree is sorted from small to large!!!
  • Disadvantages: if the data is inserted directly according to the sorted order, the binary search tree will degenerate into a linked list!!! The root cause is that the binary search tree does not automatically balance and cannot dynamically select the root node.

BSTree.h

#pragma once
#include<iostream>
enum Boolean { FALSE, TRUE }; //Custom Boolean class

template<class T>
class Element {  //More data can be added and adjusted flexibly
public:
	T key;
};

template <class T> class BSTree; //Join the declaration of BSTree to prevent errors when declaring friends

template<class T>
class BSNode {
	friend class BSTree<T>; //Make BSTree a friend to access private members
public:
	Element<T> data;
	BSNode<T> *left;
	BSNode<T> *right;
	void display(int i);
};

template <class T>
class BSTree
{
private:
	BSNode<T> *root;
public:
	BSTree(BSNode<T>*init = nullptr) { root = init; }
	~BSTree() {
		clear(root); root = nullptr;
	}
	void clear(BSNode<T>*node);
	Boolean Insert(const Element<T> &data);
	BSNode<T> *Search(const Element<T> &data);
	BSNode<T> *Search(BSNode<T>*, const Element<T> &data);
	BSNode<T> *IterSearch(const Element<T>&data);
	BSNode<T> *remove(Element<T> &data);
	BSNode<T> *remove(BSNode<T> *node, Element<T> &data);
	void MidOrder();
	void MidOrder(BSNode<T>* node);
	void display();
};

template<class T>
void BSTree<T>::clear(BSNode<T>* node) {
	if (node){
		clear(node->left);
		clear(node->right);
		delete node;
		node = nullptr;
	}
}

template <class T>
void BSNode<T>::display(int i)
{
	std::cout << "Position: " << i; // i is the location of the node, traversing down the number according to the sequence
	std::cout << "   DataKey: " << data.key << std::endl;  // Display data
	if (left) left->display(2 * i);
	if (right) right->display(2 * i + 1);
}

template<class T>
Boolean BSTree<T>::Insert(const Element<T> &data) {
	BSNode<T> *p = root;       // Current node
	BSNode<T> *q = nullptr;    // Used to point to the parent of the current node
	//You need to find before insert ing
	while (p) {
		q = p; //Before each change of p, record with q
		if (data.key == p->data.key) return FALSE;//Insert failure
		else if (data.key < p->data.key) { p = p->left; }//Towards the left
		else p = p->right; //Towards the right
	}
	//The position found after the end of the loop is q
	p = new BSNode<T>;
	p->left = p->right = nullptr;
	p->data = data;
	if (!root) root = p;
	else if (data.key < q->data.key) q->left = p;
	else q->right = p;
	return TRUE;//Indicates successful insertion
}

template <class T>
BSNode<T> * BSTree<T>::remove(Element<T> &data) {
	root = remove(root, data);
	return root;
}

// Delete node and return the deleted node
template<class T>
BSNode<T> * BSTree<T>::remove(BSNode<T> *node, Element<T> &data) {
	if (node == nullptr) return node;
	if (data.key < node->data.key)
		node->left = remove(node->left, data);
	else if (data.key > node->data.key)
		node->right = remove(node->right, data);
	else
	{ //Node to be deleted found
		if (node->left != nullptr &&  node->right != nullptr) //If the node degree to be deleted is 2
		{
			BSNode<T> * maxleft = node->left; //Find the maximum value of the left subtree to replace the node to be deleted
			while (maxleft->right != nullptr)
			{
				maxleft = maxleft->right;
			}
			node->data = maxleft->data; // replace
			node->left = remove(node->left, data); // Delete Max node
		}
		else // The degree of the node to be deleted is 0 or 1
		{
			BSNode<T> *temp = nullptr;
			if (node->left == nullptr) {
				temp = node->right;
				delete node;
				return temp;
			}
			else if(node->right == nullptr)
			{
				temp = node->left;
				delete node;
				return temp;
			}
		}
	}
	return node;
}


template<class T>
void BSTree<T>::display() {
	if (root) {
		root->display(1);
	}
	else std::cout << "empty tree\n";
}

// Recursive search
template<class T>
BSNode<T>* BSTree<T>::Search(const Element<T> &data) {
	return Search(root, data);
}
template<class T>
BSNode<T>* BSTree<T>::Search(BSNode<T>* node, const Element<T> &data) {
	if (!node) return nullptr;
	if (node->data.key == data.key) return node;
	else if ((node->data.key < data.key))
	{
		return Search(node->right, data);
	}
	else
	{
		return Search(node->left, data);
	}
}

//Iterative search
template<class T>
BSNode<T>* BSTree<T>::IterSearch(const Element<T>&data) {
	for (BSNode<T>*node = root; node;)
	{
		if (node->data.key == data.key) return node;
		else if (node->data.key < data.key) node = node->right;
		else node = node->left;
	}
}

template <class T>
void BSTree<T>::MidOrder() {
	MidOrder(root);
}

template <class T>
void BSTree<T>::MidOrder(BSNode<T>* node) {
	if (!node) return;
	MidOrder(node->left);
	std::cout << node->data.key << std::endl;
	MidOrder(node->right);
}

main.cpp

#include<iostream>
#include "BSTree.h"
using namespace std;

int main(int argc, char**argv) {
	BSTree<int> tree;
	Element<int> a, b, c, d, e, f, g, h;
	a.key = 5; b.key = 3; c.key = 11;
	d.key = 3; e.key = 15; f.key = 2;
	g.key = 8; h.key = 22;

	tree.Insert(a); tree.Insert(b); tree.Insert(c); tree.Insert(d);
	tree.Insert(e); tree.Insert(f); tree.Insert(g); tree.Insert(h);

	tree.display();
	BSNode<int> *p = tree.Search(g);
	cout << "finding result = " << p->data.key << endl;
	BSNode<int> *p2 = tree.IterSearch(g);
	cout << "finding result = " << p2->data.key << endl;
	cout << "----------------------" << endl;
	tree.MidOrder();
	cout << "delete b=3 " << endl;
	tree.remove(b);
	tree.MidOrder();
	cout << "delete f=2" << endl;
	tree.remove(f);
	tree.MidOrder();
	cout << "delete e=15 " << endl;
	tree.remove(e);
	tree.MidOrder();
	system("pause");
	return 0;
}

AVL balanced binary search tree: self balancing

Basic: Adelson velsky Landis tree

Balance: when the number of nodes is fixed, the closer the height of the left and right subtrees is, the more balanced the trees are

Balance factor: height difference between left and right subtrees of a node

Characteristics of AVL tree: the balance factor of each node can only be 1, 0 and - 1, otherwise it will be unbalanced

Add nodes to make AVL unbalanced: worst case: may cause all ancestor nodes to be unbalanced. Neither the parent node nor the non ancestor node can be out of balance. Only all the ancestor nodes above G will be out of balance

Four attitudes of AVL tree imbalance

LL: left subtree of left child with right-handed g: g causes imbalance

RR: right subtree of right child with left-handed g: g causes imbalance

LR: left p + right g = RR+LL

RL: right p + left g = ll + RR

Add AVL tree

1, General idea
  1. Insert node w as standard BST
  2. Starting from w, move up to find the first unbalanced node, g, and then determine p and n. Note: the method of looking up here is recursion!!!
  3. Fix g to make it balanced (make its height consistent with the height before insertion). gpn can be arranged in four ways
    • LL: right-handed g
    • LR: left-handed p + right-handed g
    • RR: levo g
    • RL: right p + left g
2, Specific implementation process
  • Perform normal BST insertion
  • The current node must be the ancestor of the newly inserted node. Update the height of the current node
  • Get the balance factor of the current node
  • If the balance factor is greater than 1, then the imbalance is in the case of LL or LR. By comparing the size of the insertion node and the root node of the left subtree, we can determine LL and LR
  • If the balance factor is less than - 1, then the imbalance is in the situation of RR or RL. By comparing the situation of the insertion node and the root node of the right subtree, the situation of RR or RL can be determined.
3, Practical example:

Delete AVL tree

1, General idea
  1. Delete node w with standard BST

  2. Starting from w, move upward to find the first unbalanced node g, then the corresponding node pn can be found at the same time. Note that the pn here is different from the insertion point

  3. Adjust g to balance. There are four cases, LL, LR, RR and RL. However, even after G is balanced, its ancestors are not necessarily balanced (because the height after balance is not the original height). This is different from the case of insertion, so it is necessary to repair G's ancestors up to the root node, for example:

2, Specific process
  1. Perform normal BST delete
    1. Find the node to be deleted
    2. If the degree of the node to be deleted is 2, find the minimum value of the right subtree of the node to be deleted to replace the node to be deleted, and delete the replaced node
    3. If the node to be deleted is 0 or 1, delete the node to be deleted
  2. Update the height of the current node. The current node here refers to the ancestor of all nodes to be deleted. Note that the height cannot and does not need to be updated for empty nodes
  3. Obtain the balance factor of the current node to verify whether it is unbalanced
  4. If the balance factor is more than 1, it is in the left imbalance situation, which can be divided into ll and LR. By obtaining the balance factor of the left subtree > = 0, it is LL, otherwise it is LR
  5. If the balance factor is less than - 1, it is in the right imbalance situation, which can be divided into RR and RL. By obtaining the balance factor of the right subtree > 0, it is RL, otherwise it is RR. Note that the equilibrium factor = 0 applies to RR and LL.

AVLTree.h

#pragma once
#include<iostream>
template<class T>
class AVLNode {
public:
	T key;
	int height;
	AVLNode * left;
	AVLNode * right;
	AVLNode(T value, AVLNode *l, AVLNode *r) :
		key(value), height(0), left(l), right(r) {}
};

template<class T>
class AVLTree
{
public:
	AVLTree():root(nullptr){}
	AVLTree(AVLNode<T> *r) :root(r) {}
	~AVLTree() { clear(root); root = nullptr; }
	int height();                 // Get the height of the tree
	AVLNode<T> * insert(T key);
	AVLNode<T> * remove(T key);

	// Delete AVL tree
	void clear(AVLNode<T> *root);

	void preOrder();
	void midOrder();
	void postOrder();

private:
	AVLNode<T> *root;
	int height(AVLNode<T> *node); // Get the height of the node
	int max(int a, int b);
	int getBalance(AVLNode<T> *node); // Get balance factor

	// Four ways of self balance
	AVLNode<T> *  LLrotation(AVLNode<T>* node);
	AVLNode<T> *  RRrotation(AVLNode<T>* node);
	AVLNode<T> *  LRrotation(AVLNode<T>* node);
	AVLNode<T> *  RLrotation(AVLNode<T>* node);

	// Insert Knot
	AVLNode<T> * insert(AVLNode<T> * root, T key);
	// Delete node
	AVLNode<T> * remove(AVLNode<T> *root, T key);

	void preOrder(AVLNode<T> *node);
	void midOrder(AVLNode<T> *node);
	void postOrder(AVLNode<T> *node);
};

/**********************Get the height of AVL tree*******************/
template<class T> int AVLTree<T>::height(AVLNode<T> *node){
	if (node == nullptr) return 0;
	return node->height;
}
template<class T> int AVLTree<T>::height() {
	return height(root);
}
/**********************Obtain the balance factor of AVL node*******************/
template<class T> int AVLTree<T>::getBalance(AVLNode<T>*node) {
	if (node == nullptr) return 0;
	return height(node->left) - height(node->right);
}
/**********************Update node comparison size*******************/
template<class T> int AVLTree<T>::max(int a, int b) {
	return (a > b) ? a : b;
}

/************AVL The way of self balance corresponding to four attitude of tree unbalance***********/
template<class T> AVLNode<T> * AVLTree<T>::LLrotation(AVLNode<T>* g) {
	// Turn right grandfather node, let grandfather node go down
	// g: Grandfather node (imbalance node) p: parent node 
	AVLNode<T> *p = g->left;
	g->left = p->right;
	p->right = g;
	// Update node height
	g->height = 1+max(height(g->left), height(g->right));
	p->height = 1+max(height(p->left), height(p->right));

	return p;
}
template<class T> AVLNode<T> * AVLTree<T>::RRrotation(AVLNode<T> *g) {
	// Turn left grandfather node, let grandfather node go down
	// g: Grandfather node (imbalance node) p: parent node 
	AVLNode<T> *p = g->right;
	g->right = p->left;
	p->left = g;
	// Update node height
	g->height = 1 + max(height(g->left), height(g->right));
	p->height = 1 + max(height(p->left), height(p->right));

	return p;
}
template<class T> AVLNode<T> * AVLTree<T>::LRrotation(AVLNode<T> *g) {
	// Left p + right g = RR + LL
	// g: Grandfather node (imbalance node) p: parent node 
	g->left = RRrotation(g->left);
	return LLrotation(g);
}
template<class T> AVLNode<T> * AVLTree<T>::RLrotation(AVLNode<T> *g) {
	// Right p + left g = LL + RR
	// g: Grandfather node (imbalance node) p: parent node 
	g->right = LLrotation(g->right);
	return RRrotation(g);;
}

/**********************AVL Node insertion*******************/
template<class T> AVLNode<T> * AVLTree<T>::insert(AVLNode<T> * node, T key) {
	// 1. Perform standard BST insertion
	if (node == nullptr)
		return new AVLNode<T>(key, nullptr, nullptr);
	else if (key < node->key)
		node->left = insert(node->left, key);
	else if (key > node->key)
		node->right = insert(node->right, key);
	else
		return node;// Duplicate nodes are not allowed to be inserted
	
	// 2. Update the height of the ancestor node
	node->height = max(height(node->left), height(node->right)) + 1;

	// 3. Obtain the balance factor and judge whether it is unbalanced
	int balance = getBalance(node);
	// 4. In case of imbalance, judge the four imbalances
	if (balance > 1 && key < node->left->key) return LLrotation(node);
	if (balance > 1 && key > node->left->key) return LRrotation(node);
	if (balance < -1 && key > node->right->key) return RRrotation(node);
	if (balance < -1 && key < node->right->key) return RLrotation(node);

	return node;
}  

template<class T> AVLNode<T> * AVLTree<T>::insert(T key) {
	root =  insert(root, key);
	return root;
}

/**********************AVL Delete tree*******************/
template<class T> void AVLTree<T>::clear(AVLNode<T> *node) {
	if (node != nullptr) {
		clear(node->left);
		clear(node->right);
		delete node;
		node = nullptr;
	}
}

/**********************AVL Node deletion*******************/
template <class T> AVLNode<T> * AVLTree<T>::remove(T key) {
	root = remove(root, key);
	return root;
}

// Delete the node and return the root of the modified subtree
template<class T> AVLNode<T> * AVLTree<T>::remove(AVLNode<T> *node, T key) {
	// 1. Delete the implementation standard BST
	if (node == nullptr) {
		std::cout << "Node does not exist and cannot be deleted\n";
		return nullptr;
	}
	if (key < node->key){
		node->left = remove(node->left, key);
	}
	else if (key > node->key) {
		node->right = remove(node->right, key);
	}
	else{
		// Found the node to be deleted and started classification discussion
		// If the degree of deleting node is 2
		if (node->left != nullptr && node->right != nullptr) {
			// Find the minimum value of the right subtree of the node to be deleted to replace the node to be deleted
			AVLNode<T> * minright = node->right;
			while (minright->left){
				minright = minright->left;
			}
			node->key = minright->key;  // Replace node to be deleted
			node->right = remove(node->right, minright->key); // Delete replaced node
		}
		else // If the degree of the node to be deleted is 0 or 1, delete the node to be deleted
		{   
			// And set the node as a non empty child node (degree 1)
			// Or set to null pointer (degree 0)
			AVLNode<T> * temp = node;
			node = node->left ? node->left : node->right;
			delete temp;
		}
	}

	// 2. Update the height of the current node
	if (node == nullptr) return nullptr; //Height cannot and does not need to be updated for empty nodes
	node->height = 1 + max(height(node->left), height(node->right));

	// 3. Obtain the balance factor of the current node and verify whether it is unbalanced
	int balance = getBalance(node);
	// 4. Four cases of restoring balance
	if (balance > 1) {  //Left side imbalance, can be divided into LR,LL
		if (getBalance(node->left) >= 0) return LLrotation(node);
		else return LRrotation(node);
	}
	if (balance < -1) { // The right side imbalance can be divided into RL and RR
		if (getBalance(node->right) > 0) return RLrotation(node);
		else return RRrotation(node);
	}
	return node;
}

/**********************Various traversals of trees*************************************/
template<class T> void AVLTree<T>::preOrder(AVLNode<T> *node) {
	if (node) {
		std::cout << node->key << " "; preOrder(node->left); preOrder(node->right);
	}
}

template<class T> void AVLTree<T>::midOrder(AVLNode<T> *node) {
	if (node){
		midOrder(node->left); std::cout << node->key << " "; midOrder(node->right);
	}	
}

template<class T> void AVLTree<T>::postOrder(AVLNode<T> *node) {
	if (node) {
		postOrder(node->left); postOrder(node->right); std::cout << node->key<<" ";
	}
}
template<class T> void AVLTree<T>::preOrder() { preOrder(root); }
template<class T> void AVLTree<T>::midOrder() { midOrder(root); }
template<class T> void AVLTree<T>::postOrder() { postOrder(root); }

Random test code main.cpp

#include<iostream>
#include<time.h>
#include<vector>
#include "AVLTree.h"
using namespace std;

int main(int argc, char**argv) {
	srand((int)time(0));
	AVLTree<int> tree;
	int times = rand()%20; //Number of nodes in Random Spanning Tree
	int copytimes = times;
	vector<int> arr;
	while (copytimes--)
	{
		int val = rand() % 100;
		arr.push_back(val);
		tree.insert(val); // Randomly insert the size of any node, with the range of 0-99
	}
	tree.midOrder();
	cout << "**************" << endl;
	while (times--)
	{
		int todel = rand() % arr.size();
		cout << "\n try to del " << arr[todel] << endl;
		tree.remove(arr[todel]);
		cout << " the result is ";
		tree.midOrder();
	}
	cout << "\n***********" << endl;
	system("pause");
	return 0;
} 

Tree B: self balancing

B-tree is a balanced multi-path search tree (multi fork tree), which is mostly used in file system and database implementation, and can reduce the number of disk access.

1, Why B tree?

Disk structure

A circle can be divided according to sector or circular orbit. Block id = sector ID + track ID. If the block has a fixed size, such as 512Bytes, the location of any byte can be defined by the subscript in the block.


Assuming that one piece of information in the database is 128bytes, a 512bytes block can store four pieces of information. A total of 25 blocks are needed for 100 pieces of database information.

When searching for information, you have to traverse 25 blocks. This time can be reduced. As long as an index table is added to the current database, the table memory stores id and pointer information. Assuming the size of each information in the index table is 16, a block can store 512 / 16 = 32 pieces of information in total. Then 100 pieces of information need 100 / 32 ~ = 4 pieces of information to store the index table. In addition, 4 + 1 pieces of information need to be accessed in total. If you expand the number of records to 1000, the index table will occupy more blocks, which will take 40 blocks. The index of these 40 blocks will take a lot of time, so you can speed up the search by creating the index table of the index table. For example, to create a new index table, the first id and address of each block are stored in each index table, then only two blocks are needed to store the index table (that is, sparse table).

To sum up, the retrieval speed can be accelerated through multi-layer index, as shown in the figure, which is the basis of B-tree and B + tree. In the B tree, all nodes have multiple pointers to elements in the database

M-way search tree

A node can have n key s, and each node can have n+1 number of child nodes. m-way search tree uses m=n+1 to represent the number of paths to the child node.

There are some problems in the M-way search tree. For example, for the 10-way search tree, there are 10, 20, 30 key s. When inserting these three nodes, the following bad situation may occur, because the M-way search tree has no insertion rules. B-tree is the M-way search tree with these rules


For example, it specifies that all non leaf nodes except root nodes must be half filled to create child nodes, which can eliminate the problem of m-ways tree

2, Rules of B-tree

There are two versions of B-tree rules: the version with the minimum degree is considered from the perspective of keys, and the data is clearer; the version with the specified degree is considered from the perspective of nodes, which is easier to understand.

Specify the minimum degree of B tree as t: from the perspective of keys

  • All nodes have at most 2t-1 keys, that is, at most 2T child nodes

  • If the tree is not empty, the root has at least one keys

  • Number of childnodes of a node = number of keys + 1

Specify the order of B-tree as m: from the perspective of nodes

  • Order m indicates that each node has at most M child nodes

  • If the child nodes of the non root node are k, then M / 2 < = k < = m (it is required to fill half of them to create a child node, that is, the non leaf node must have at least ceil(m/2)-1 keys)

  • The root node must have at least 2 childnodes (if not the leaf node)

  • All the leaves are on the same layer

  • Insert from bottom to top

For example: when t = 2: all nodes can have at most four children, and non root nodes have at least two children. Therefore, the number of children of non root nodes has three possibilities: 2, 3, and 4, which is called 2-3-4 tree.

2.1 search of B tree

Find k from the root node, set a subscript i=0, determine the subscript range of k in the node by comparing the size, and set the size as the number of keys owned by the current node.

  • If I < size and K = = node - > keys [i] can be found, then
  • Otherwise, if the node is a leaf node, it is not
  • Otherwise, k is between keys[i-1] and keys[i], and the two adjacent keys need to continue recursive traversal.
2.2 traversal of B tree

Start with the leftmost child, print the leftmost child recursively, and repeat the same process for the remaining childnodes and keys. Finally, recursively print the rightmost child

2.3 insertion of B-tree
Method 1:

Similar to BST, it traverses from root to leaf node. After arriving at the leaf, insert the key into the childnodes. The problem is that the keys that can be accommodated in the child nodes are fixed. When inserting a new key, it must be ensured that the child nodes have enough space to accommodate the key. This can be achieved by splitting the nodes before inserting.

Therefore, to insert a key, you need to traverse from root to leaf, and check whether the node is full when traversing. If it is full, split the node to create space. The disadvantage is that unnecessary splitting may occur

Example

Method two:
  • Find the leaf node where X should be added

  • Add X to the appropriate location between existing values. As a leaf node, there is no need to worry about the subtree

  • If after adding X, the number of keys in the node is < = 2 * T-1, there is no problem. Otherwise, the node overflows and begins to split, dividing the node into three parts

    • The original node stores t-1 keys on the left
    • Create a new node to store t-1 keys on the right
    • The middle key is placed on the parent node
  • Determine whether the parent node overflows until no overflow occurs

Example

Create a fourth-order B-tree with nodes of 10, 20, 40, 50, 60, 70, 80, 30, 35, 5, 15

To put it simply, when it's full, pull out one and put it on it. The other data is divided into two pieces. The nodes of save 40 are created after inserting 50, so the B-tree is created from the bottom up

Keep adding 60, 70, 80. It's changed

Keep adding 30 to 35. It's changed

Keep adding 5 to 15. It's changed

2.4 delete B tree:
Classified discussion
  • If the deleted key is in the leaf

    • If n > T-1 in leaf, delete it directly

    • If n = t-1 in leaf,

      • "Borrow": if one of the two adjacent left and right sibling nodes is n > T-1, borrow from the sibling node by taking one from the parent node of the current node and one from the sibling node of the parent node
      • "Merge": if both adjacent left and right brothers are n = t -1, the leaf and left brothers will be
        (or brothers) and the middle key in the parent are merged into a node, and the key is deleted
      • "One side": for leaf on the edge, only one side node is considered to meet the condition of N > T-1, which satisfies: borrow, not: merge

      Note: after merging, the parent node will have one less key. If the parent node n < T-1 after merging, you need to borrow or merge the parent node repeatedly

  • If the deleted key is in the intermediate node

    • If the maximum value is found in the subtree with the root as the precursor node, or the minimum value is found in the subtree with the root as the successor node, and the value is n > T-1 of the leaf node, the value is exchanged with the key, and then the key is deleted directly
    • If the leaf nodes found by the precursor and the successor satisfy n = t-1, the precursor and the successor are merged and the key is deleted
  • In the actual operation, the nodes are inflated and deleted to ensure the balance.

Example

Original graph

If n > T-1 in leaf, delete it directly

Delete 65: n > T-1, delete directly

If n = t-1 in leaf, if one of the left and right sibling nodes is n > T-1, borrow from sibling node. The borrowing method is that the current node takes one from parent node and the parent node takes one from sibling node.

Delete 23, n = T-1, left sibling n > T-1, take one from left sibling

Delete 72, n = T-1, right sibling n > T-1, take one from right sibling

If n = t-1 in leaf, if both the left and right brothers are n = t-1, select any brother to merge with it, and the key between the two nodes mediated by the parent node, merge together, and then delete the node to be deleted

Delete 64, n = T-1, left & right n = T-1, merge (this, leftsibling), delete key

The processing of leaf located at the edge of the parent node is similar, except that the selection of borrowing or merging can only be unilateral. If the N < T-1 of the parent node after merging, repeat the operation for the parent node.

If the deleted key is in the intermediate node

  • If the maximum value is found in the subtree with the root as the precursor node, or the minimum value is found in the subtree with the root as the successor node, and the value is n > T-1 of the leaf node, the value is exchanged with the key, and then the key is deleted directly

    Delete 70, 95, corresponding precursor and successor n > T-1, then exchange and delete directly

  • If the leaf nodes found by the precursor and the successor satisfy n = t-1, the precursor and the successor are merged and the key is deleted

    Delete 77, predecessor and successor n = t-1

    Delete 80, max. precursor 79 n > T-1

Delete 100, left and right n = t-1. After merging, it is necessary to discuss whether the parent node conforms to the rules

Collapse:

Delete 6, 27, 60, 16, then 50

Implementation method

3, c + + implementation of B tree

Animation: https://www.cs.usfca.edu/~galles/visualization/BTree.html

3.1 BTree.h
#pragma once
#include<iostream>
// The minimum degree of B-tree is m
template <class T,int m = 3>
class BNode {
public:
	int n;       // Number of key s currently stored
	T keys[2*m-1];  // Pointer to the keys array
	BNode<T>* childnodes[2 * m]; // Save childpointers
	bool isleaf;    // True if the node is a leaf node, false otherwise

	BNode(int _n=0,bool _isleaf=true):n(_n),isleaf(_isleaf){
		for (int i = 0; i < 2*m; i++)
		{
			childnodes[i] = nullptr;
		}
	}	
};

template <class T,int m=3>
class BTree
{
public:
	BNode<T,m> * root;
	void traverse(BNode<T,m>* node); // ergodic
public:
	BTree():root(nullptr) {}
	BNode<T, m>* search(BNode<T, m>* node, T key);
	// Insertion function
	void splitchild(BNode<T, m>* parentnode, int index);
	void insert(T key);
	void insert_nonfull(BNode<T, m>*node, T key);

	void traverse(); // ergodic

	// Delete function
	void remove(const T& key);
	void remove(BNode<T,m>*node, const T & key);
	int findKey(BNode<T, m>*node, const T & key);
	bool leakfill(BNode<T, m>*node, int id);
	void removeFromLeaf(BNode<T, m>*node,int id);
	void removeFromNonLeaf(BNode<T, m>*node, int id);
	BNode<T, m>* getPred(BNode<T,m>*node,int id); // Gain the precursor
	BNode<T, m>* getSucc(BNode<T, m>*node, int id); // Get successor
	void borrowFromPrev(BNode<T, m>*node, int id);
	void borrowFromNext(BNode<T, m>*node, int id);
	void merge(BNode<T, m>*node, int id);
};



/*****************************B Tree search*********************************/
template<class T,int m>
BNode<T, m>* BTree<T,m>::search(BNode<T, m>* node, T key) {
	if (node == nullptr) return nullptr;
	// Find the range of key in the current node, and make n+1 branch selection for each internal node
	int i = 0;  // Start subscript from 0
	while (i < node->n && key > node->keys[i]) { i++; } 

	//Discussion of subscripts by category
	if ( key == node->keys[i]) return node; // 1. Found. Return address
	if (node->isleaf) return nullptr;       // 2. It's not found and it's a leaf. It's not found completely. Return to null
	return search(node->childnodes[i], key);// 3. It is not found, and it is not a leaf. Continue to search
}

/*****************************B Tree insertion*********************************/
template <class T,int m> 
void BTree<T, m>::splitchild(BNode<T, m>*node, int i) {
	// Function: split the full child nodes of the node, and adjust the middle key to the node
	// Node: not full node I: subscript of full child node L of node
	// m: Minimum degree
	// L: full childnodes with I as the subscript i n node, that is, n = 2*t-1, subscript from 0 to 2 * T-2
	// There are 2*t-1 keys in L. leave the left t-1 keys for L, the right t-1 keys for R, and the middle key for node
	// R: new node split by L, n will be set to t-1, subscript from 0 to T-2
	BNode<T,m> * R = new BNode<T>();  
	BNode<T,m> * L = node->childnodes[i]; // Original full child
	

	// 1. Copy keys and nodes from L to construct R
	R->isleaf = L->isleaf;
	R->n = m - 1; 
	for (int j = 0; j < m-1; j++) 
		R->keys[j] = L->keys[m + j];
	if (L->isleaf==false){
		for (int j = 0; j < m; j++)
			R->childnodes[j] = L->childnodes[j + m];
	}
	L->n = m - 1; // Modify the number of keys in L
	
	// 2. Promote the intermediate node to the parent node
	for (int j = node->n ; j > i; j--)	{
		// Starting from the tail node with subscript x - > N, all the pointers before i do not move,
		//All the pointers after i move backward, leaving the pointer position of i+1 to link the new node
		node->childnodes[j+1] = node->childnodes[j];
	}
	node->childnodes[i + 1] = R;

	for (int j = node->n;j>i;j--) { 
		//The number of key moves is the same as that of childnodes
		node->keys[j] = node->keys[j-1];
	}
	node->keys[i] = L->keys[m - 1]; //Put the middle value key of L on the corresponding space of the parent node

	node->n++; // Update the number of keys in the node
}

template<class T, int m>
void BTree<T, m>::insert_nonfull(BNode<T,m>*node,T key) {
	int i = node->n - 1; 
	if (node->isleaf)
	{  // Insert it into the leaf node that is not full, and insert it directly in the vacated position
		while (i>=0 && key < node->keys[i])
		{
			node->keys[i+1] = node->keys[i];
			i--;
		}
		node->keys[i+1] = key;
		node->n++;
	}
	else
	{
		// To insert into a non leaf node that is not full, you need to enter the child nodes to insert
		//Find the key in the child nodes [i + 1] between keys[i] and keys[i+1]
		while (i >= 0 && key < node->keys[i]) --i;
		if (node->childnodes[i+1]->n == 2*m-1)
		{	// If it is full, the node will be split, and a new value will be inserted in the keys[i+1] position
			splitchild(node, i + 1);
			// Judge whether the key is on the left node[i+1] or the right node[i+2] after split according to the size of the new value
			if (node->keys[i + 1] < key) i++;
		}
		insert_nonfull(node->childnodes[i+1], key);
		// Enter into the child node and continue to insert, in essence, recurse to the leaf node
	}
}

template <class T,int m>
void BTree<T, m>::insert(T key) {
	// Insertion of empty tree
	if (root == nullptr) {
		root = new BNode<T, m>();
		root->keys[0] = key;
		root->n = 1;
	}
	// If the tree is not empty
	else {
		if (root->n == 2*m-1) // If the root node is full, you need to manually create the parent node of the root node to call splitchild
		{	BNode<T, m> *oldroot = root;
			BNode<T, m> *newroot = new BNode<T, m>();
			root = newroot;
			newroot->childnodes[0] = oldroot; 
			// 0 because the parent of the root is empty
			splitchild(newroot, 0); //Split the oldroot, and then there will be another number on the newroot
			root->isleaf = false;
			insert_nonfull(newroot, key); // Insert the new tree whose root node is not full again
		}
		else insert_nonfull(root, key); // Go to the leaf node
	}
}

/*****************************B Traversal of trees*********************************/
template<class T, int m> void BTree<T, m>::traverse() {
	traverse(root);
}

template<class T,int m>
void  BTree<T, m>::traverse(BNode<T,m>* node) {
	if (node == nullptr) return;
	int i = 0;
	for (; i < node->n; i++)
	{
		if (node->isleaf==false) // If it is not a leaf node, you need to print the child nodes to the left of the current key before printing
		{
			traverse(node->childnodes[i]);
		}
		std::cout << " " << node->keys[i]; //Print the key where i is
	}
	// Print separately for the rightmost childnodes
	if (node->childnodes[i] != nullptr) traverse(node->childnodes[i]);
}


/*****************************B Tree deletion*********************************/
template<class T, int m>
void BTree<T, m>::remove(const T&key) {
	remove(root, key);
}
// Find I of node - > keys [i] > = key
template<class T, int m>
int BTree<T, m>::findKey(BNode<T, m>*node, const T &key) {
	int id = 0;
	while (id<node->n && key>node->keys[id]) id++;
	return id;
}

template<class T, int m>
void BTree<T, m>::remove(BNode<T, m>*node, const T &key) {
	if (node == nullptr) return;
	int id = findKey(node, key);
	if (id < node->n && node->keys[id] == key)
	{   // For the case of finding nodes
		if (node->isleaf)
			removeFromLeaf(node, id);
		else
			removeFromNonLeaf(node, id);
	}
	else // For the case where no node has been found
	{
		if (node->isleaf) { //If it is a leaf, the key is not in the tree
			std::cout << key << " does not exist\n";
			return;
		}
		// It is likely that the nodes going on will be out of balance, so we need to let them expand first,
		// However, it should be noted that the id will change after inflation, so it is necessary to analyze the possible inflation
		// To change the id of a key, it must be because the subscript of the node has changed,
		// In leakfill, borrow does not affect the subscript change of node, only merge may change the subscript
		// When the node and the right node are merged, the id will not change
		// Only the rightmost node. After merging with the left node, the subscript id in the node will be reduced by 1

		bool flag = false; // No consolidation by default
		if (node->childnodes[id]->n < m)
			flag = leakfill(node, id);//If a merger occurs
		if (flag)
			remove(node->childnodes[id - 1], key);
		else
			remove(node->childnodes[id], key);
	}
}

// Call this function to prevent node underflow
// Return true, indicating that the rightmost node and its left brother have merged, and the retrieval id will be - 1
template<class T, int m>
bool BTree<T, m>::leakfill(BNode<T, m>*node, int id) {
	// The first kind: borrow from the left brother node
	if (id != 0 && node->childnodes[id - 1]->n >= m)
		borrowFromPrev(node, id);
	// Second: borrow from right brother node
	else if (id != node->n && node->childnodes[id + 1]->n >= m)
		borrowFromNext(node, id);
	// Third, the child nodes [ID] and brother nodes are merged
	else
	{
		if (id < node->n)
			merge(node, id); //If not the rightmost node, merge with the right node
		else {
			merge(node, id - 1); // Otherwise, merge with the left node
			return true;
		}
	}
	return false;
}

template<class T, int m>
void BTree<T, m>::removeFromLeaf(BNode<T, m>*node, int id) {
	for (int i = id + 1; i < node->n; i++)
		node->keys[i - 1] = node->keys[i];
	node->n--;
}

template<class T, int m>
void BTree<T, m>::removeFromNonLeaf(BNode<T, m>*node, int id) {
	// Currently, node - > keys [ID], the precursor is node - > childNodes [ID], and the successor is node - > childNodes [ID + 1]
	// To determine whether the precursor meets n > T-1, replace it and then delete it
	T key = node->keys[id];
	if (node->childnodes[id]->n > m - 1)
	{
		BNode<T, m> *pred = getPred(node, id);
		node->keys[id] = pred->keys[pred->n - 1];
		remove(pred, pred->keys[pred->n - 1]);
	}// If the subsequent n > T-1 is satisfied, replace it and delete it
	else if (node->childnodes[id + 1]->n > m - 1)
	{
		BNode<T, m> *succ = getSucc(node, id);
		node->keys[id] = succ->keys[0];
		remove(succ, succ->keys[0]);
	}
	else //If both the predecessor and the successor satisfy n = t-1, the predecessor successor and the parent key are merged
	{
		merge(node, id);
		remove(node->childnodes[id], key);
	}
}

// Obtain the node where the precursor maximum value of node - > keys [ID] is located
template<class T, int m>
BNode<T, m>* BTree<T, m>::getPred(BNode<T, m>*node, int id) {
	BNode<T, m> *cur = node->childnodes[id];  // cur is the precursor of node - > keys [ID]
	//Continue to the right to find the maximum value in the leaf node
	while (!cur->isleaf) {
		cur = cur->childnodes[cur->n];
	}
	return cur;
}
// Get the node where the subsequent minimum value of node - > keys [ID] is located
template<class T, int m>
BNode<T, m>* BTree<T, m>::getSucc(BNode<T, m>*node, int id) {
	BNode<T, m> *cur = node->childnodes[id + 1]; // cur is the successor of node - > keys [ID]
	// Continue to the left to find the minimum value in the leaf node
	while (!cur->isleaf)
	{
		cur = cur->childnodes[0];
	}
	return cur;
}


/****node->childnodes[ id ]->n  < t-1  Borrow from brothers or merge with them******/
template<class T, int m>
void BTree<T, m>::borrowFromPrev(BNode<T, m>*node, int id) {
	// node->childnodes[ id ]->n  < t-1 
	// node->childnodes[id-1]->n  > t-1
	// [id] to node, node to [id-1]
	BNode<T, m> *child_i = node->childnodes[id];
	BNode<T, m> *sibling = node->childnodes[id - 1];

	//Make room for the key in the node
	for (int i = child_i->n; i > 0; i--) {
		child_i->keys[i] = child_i->keys[i - 1];
	}
	//Non leaf node, the child's pointer also needs to move with it
	if (!child_i->isleaf) {
		for (int i = node->childnodes[id]->n; i >= 0; i--) {

			child_i->childnodes[i + 1] = child_i->childnodes[i];
		}
	}
	child_i->keys[0] = node->keys[id - 1];

	//Let the rightmost child nodes of sibling be the first child nodes of child
	if (!child_i->isleaf) {
		child_i->childnodes[0] = sibling->childnodes[sibling->n];
	}

	node->keys[id - 1] = sibling->keys[sibling->n - 1];

	child_i->n++;
	sibling->n--;
}
template<class T, int m>
void BTree<T, m>::borrowFromNext(BNode<T, m>*node, int id) {
	// node->childnodes[ id ]->n  < t-1 
	// node->childnodes[id+1]->n  > t-1
	// [id] to node, node to [id+1]
	BNode<T, m>*child_i = node->childnodes[id];
	BNode<T, m>*sibling = node->childnodes[id + 1];

	child_i->keys[child_i->n] = node->keys[id];
	if (!child_i->isleaf)
	{
		child_i->childnodes[child_i->n + 1] = sibling->childnodes[0];
	}
	node->keys[id] = sibling->keys[0];
	for (int i = 0; i < sibling->n - 1; i++)
	{
		sibling->keys[i] = sibling->keys[i + 1];
	}
	if (!sibling->isleaf)
	{
		for (int i = 0; i <= sibling->n - 1; i++)
		{
			sibling->childnodes[i] = sibling->childnodes[i + 1];
		}
	}
	child_i->n++;
	sibling->n--;
}

template<class T, int m>
void BTree<T, m>::merge(BNode<T, m>*node, int id) {
	// node->childnodes[id] n = t-1
	// node->childnodes[id+1] n = t-1
	//Merge node - > key [ID], node - > childNodes [ID] and node - > childNodes [ID + 1]
	BNode<T, m>* child = node->childnodes[id];
	BNode<T, m>* sibling = node->childnodes[id + 1];

	// Merge the key of child\sibling\node into child
	child->keys[m - 1] = node->keys[id];
	for (int i = 0; i < sibling->n; i++)
	{
		child->keys[m + i] = sibling->keys[i];
	}
	//For non child nodes, you also need to merge child nodes
	if (!child->isleaf) {
		for (int i = 0; i < sibling->n; i++) {
			//Merging the three nodes
			child->childnodes[m + i] = sibling->childnodes[i];
		}
	}
	// Move the position after node - > keys [ID] forward
	for (int i = id; i < node->n - 1; i++)
	{
		node->keys[i] = node->keys[i + 1];
		node->childnodes[i + 1] = node->childnodes[i + 2];
	}

	child->n += sibling->n + 1;
	node->n--;
	delete sibling;
    sibling = nullptr;
}
3.2 main.cpp
#include <iostream>
#include "BTree.h"
using namespace std;

int main(int argc, char ** argv) {
	const int m = 3;
	BTree<int,m> t;
	t.insert(1);	t.insert(3);	t.insert(7);	t.insert(10);	
	t.insert(11);	t.insert(13);	t.insert(14);	t.insert(15);	
	t.insert(18);	t.insert(16);	t.insert(19);	t.insert(24);
	t.insert(25);	t.insert(26);	t.insert(21);	t.insert(4);
	t.insert(5);	t.insert(20);	t.insert(22);	t.insert(2);
	t.insert(17);	t.insert(12);	t.insert(6);
	cout << "Traversal results:  ";  t.traverse();  cout << endl;
	t.remove(16);
	cout << "Traversal results:  ";  t.traverse();  cout << endl;
	t.remove(13);
	cout << "Traversal results:  ";  t.traverse();  cout << endl;
	system("pause");
}

4, Upgrade version of B-tree

B+ tree:

The node of B tree has data information,

The node of B + tree only leads the way, and all data information is placed in the leaf node.

The pointer stored in the leaf node of B tree is nullptr,

The leaf node of the B + tree has no pointer to the child node, but has a sequential access pointer. Each leaf node has a pointer to an adjacent leaf node, forming a linear data structure at the bottom.

Because no data is stored inside the node, a node can store more key s (more records can be stored in a block), so it becomes the first choice of database index (the index of database index certainly does not contain data)

B* tree:

Half full for tree B, 2 / 3 full for tree b *

Red and black trees:

Although the height of both of them increases at the speed of O (log n), the base of logarithm in B-tree can be many times larger, so in the case of the same number of query comparisons, B-tree can avoid a large number of disk accesses.

After inserting elements into B-tree, many nodes may have chain changes, which is also the self balance advantage of B-tree

RBT of mangrove: self balance

RBT VS AVL

Red Black Tree (RBT): a balanced binary search tree with monotonous middle order traversal.

Compared with AVL tree, AVL tree is a strictly balanced binary tree with a balance factor of 0, - 1, 1, but they may cause more rotation during insertion and deletion. For example, when inserting, the end of the balance adjustment of the lower part may cause the imbalance of the upper part, which may rotate all the way to the root node. Therefore, AVL tree is suitable for frequent search operations, but not for frequent add and delete operations.

For the red black tree, the red black tree is a locally balanced binary tree, which can only ensure that one subtree of each node is not more than twice the length of the other subtree. In the operation of inserting and deleting, only two rotations and changing the color of nodes are needed at most, so the time complexity is low and it is suitable for the frequent operation of inserting and deleting. The bottom layer of set and map in c + + is red black tree.

Red and black rule

Red black tree features: nodes are divided into black and two colors. Red black rules need to be followed when inserting and deleting nodes. Red black rules ensure the balance of red black trees.

  • Self balanced BST

  • There are only red and black nodes

  • Roots are always black

  • NIL: external nodes of the red black tree

    • The leaf node is a virtual node and does not exist in the tree
    • Leaf node color must be black, no Value exists
  • Children with red nodes must be black (no two consecutive red, two consecutive black)

  • Each path from root to leaf NIL must contain the same number of black nodes

    (if the red node is removed, the tree formed by black is perfectly balanced, with the same level and depth)

inference

  • When adding nodes, red nodes are added by default, because adding red nodes does not affect the black level

  • The longest path does not exceed twice the shortest path

AVL left right review

Red black tree insertion

Method:

  1. Execute standard BST insertion, if empty tree, create black node

  2. Perform standard BST insertion. If the tree is not empty, create a red node

  3. Exit if the parent of the new node is black

  4. If the father of the new node is red (the grandfather node must be black), check the uncle node

    1. If it is red, uncles will be black. If the grandfather node is not the root node, the grandfather node will be red. Check the grandfather node (recursion) again until the root node

    2. In case of black or empty, rotation operation (LL,LR,RR,RL, same as AVL) and discoloration

    LL: right g, change g, p color

    LR: right-handed p, call LL

    RR: left g, change g, p color

    RL: left-handed p, call RR

Deletion of red black tree

double black (db): if Black is deleted and replaced with its BlackChild, the BlackChild is db. Because db will cause the number of Black nodes to be unequal on each path, this is the complexity of RBTree deletion.

Delete step: assume the node to be deleted is c

  1. Perform BST deletion of standards. If it is deleted as an intermediate node, the key replacement of pred or succ will occur, and the color will not change. In the end, all cases are converted to leaf node deletion (single child node deletion will continue to replace), which is recorded as U. Special case: if c is a leaf, u is nil.

  2. If u is Red, delete it directly;

  3. If u is Black, u is a leaf node, and its child NIL is Black, then u is db.

    • If the root is db, change it to single black directly. End

    Define the sibling s, parent p, left child X of S, right child y of S;

    • If s is black and x,y is black, then u gives p a black

      • If P is red, let p be black, s be red, and end.

      • If P is black, let p be db, s be red, and recurse the db processing of P.
    • If s is black and at least one of x,y is red, then check the color of far node far away from u in xy

      • If far is red (including near is red or black). Let s be the color of P, let p be black, let far be black, let s go up (if s is the right child, left-handed p; if s is the left child, right-handed p)

        After this adjustment, the number of black knots must be the same

      • If far is black, near is red. Let s be red, near black, and s rotate in the opposite direction to u. The case that the recursive call far is red

    • If s is red, then p,x,y must be black

      • Make s black and P red. Let s go up by rotation (s is right node, then left-handed p; s is left node, then right-handed P). After processing, the u node remains unchanged, and its sibling is no longer the original s, but x or y. The path length has not been repaired, so it enters into the case that s is black.

Tip: p must rotate in db direction and s must rotate away from db direction

db family:

If s is black, far near is black. db throws sb to p, s gets angry

If s is black, far is red, db tells p, let p inspire s, p calm down, s calm down far, p beat db

If s is black and near is red, db tells s that s calms down near, s gets angry, s thinks db is not a good person, and leaves db

If s is red, p calm s down, p get angry, p beat db

RBTree.h

#pragma once
#include<iostream>
enum COLOR {RED,BLACK};
template<class T> class RBTree;

template<class T>
class RBTNode {
private:
	T key;
	COLOR color; // Red = 0, black = 1,db = 2
	RBTNode<T> * left;
	RBTNode<T> * right;
	RBTNode<T> * parent;
public:
	RBTNode(T value):key(value),color(RED),left(nullptr),right(nullptr),parent(nullptr) {}
	friend  class RBTree<T>;
};

template<class T>
class RBTree{
private:
	RBTNode<T> * root;
public:
	RBTree() :root(nullptr) {}
	~RBTree() { clear(root); root = nullptr; }
	void clear(RBTNode<T>*node);
	void MidOrder();
	void insert(T key);
	void remove(T key);
private:
	// Ergodic exercises
	void MidOrder(RBTNode<T>* node);
	// Rotary Basic Gymnastics
	void leftrotate(RBTNode<T>*node);
	void rightrotate(RBTNode<T>*node);
	// Color based Gymnastics
	COLOR getcolor(RBTNode<T>*node);
	// Insert base exercise
	void LLinsert(RBTNode<T>*node);
	void RRinsert(RBTNode<T>*node);
	RBTNode<T>* BSTinsert(T key);
	void fixColor(RBTNode<T>* node);
	// Delete base exercises
	RBTNode<T>* getfar(RBTNode<T>*u);
	RBTNode<T>* getnear(RBTNode<T>*u);
	void farred(RBTNode<T>*u);
	void farblack(RBTNode<T>*u);
	void fixdb(RBTNode<T>*u);
	RBTNode<T>* getnodeBST(RBTNode<T>*node,T key);
	RBTNode<T>* getPred(RBTNode<T>* node);
	RBTNode<T>* getSucc(RBTNode<T>* node);
};

// Revolve x left or right with the child of X as the center
template<class T> void RBTree<T>::leftrotate(RBTNode<T> *node) {
	RBTNode<T> *child = node->right;
	if (node == nullptr || child == nullptr) return;
	//Deal with the relationship between child - > left and node first
	if(child->left != nullptr) child->left->parent = node;
	node->right = child->left;
	//Then deal with the relationship between child and node - > parent, and judge that node is the left and right children of parent
	child->parent = node->parent;
	if (node->parent == nullptr) root = child;
	else if(node == node->parent->left) node->parent->left = child;
	else node->parent->right = child;
	//Dealing with the relationship between child and node
	child->left = node;
	node->parent = child;
}
template<class T> void RBTree<T>::rightrotate(RBTNode<T> *node) {
	if (node == nullptr || node->left == nullptr) return;
	RBTNode<T>*child = node->left;
	// Deal with the relationship between child - > right and node first
	node->left = child->right;
	if (child->right != nullptr) child->right->parent = node;
	// Deal with the relationship between child and node - > parent again
	child->parent = node->parent;
	if (node->parent == nullptr) root = child;
	else if (node == node->parent->right) node->parent->right = child;
	else node->parent->left = child;
	// Finally, deal with the relationship between node and child
	node->parent = child;
	child->right = node;
}
// Color based operation, red is 0, black is 1
template<class T> COLOR RBTree<T>::getcolor(RBTNode<T>*node) {
	if (node == nullptr) return BLACK;
	return node->color;
}
/************************Insert basic operation********************************/
template<class T> void RBTree<T>::LLinsert(RBTNode<T>*node) {
	RBTNode<T> *p = node->parent;
	RBTNode<T> *g = p->parent;
	rightrotate(g);
	g->color = RED;
	p->color = BLACK;
}
template<class T> void RBTree<T>::RRinsert(RBTNode<T>*node) {
	RBTNode<T> *p = node->parent;
	RBTNode<T> *g = p->parent;
	leftrotate(g);
	g->color = RED;
	p->color = BLACK;
}
template<class T> RBTNode<T>* RBTree<T>::BSTinsert(T key) {
	RBTNode<T> * p = root;
	RBTNode<T> * q = nullptr; // Parent node of p
	while (p)
	{
		q = p;
		if (key == p->key) return nullptr;
		else if (key > p->key) p = p->right;
		else p = p->left;
	}
	p = new RBTNode<T>(key);
	if (root == nullptr) {
		root = p;
		root->color = BLACK;
	}
	else if (key < q->key) {
		q->left = p;
		p->parent = q;
	}
	else {
		q->right = p;
		p->parent = q;
	}
	return p; // Return to new node
}
template<class T> void RBTree<T>::fixColor(RBTNode<T>* node) {
	if (node == nullptr) return;
	// Node is the newly inserted node, which needs to be recolor
	RBTNode<T> *p = node->parent;
	if (node == nullptr || node == root || p->color == BLACK) return;
	//Only when parent - > color is red, parent - > parent - > color is BLACK
	RBTNode<T> *g = p->parent;
	RBTNode<T> *u = nullptr;
	if (p == g->right) u = g->left;
	else u = g->right;

	if (getcolor(u) == RED) {
		u->color = BLACK;
		p->color = BLACK;
		if (g != root) g->color = RED;
		fixColor(g);
	}
	else {
		if (g->left == p && p->left == node) {
			// LL situation
			LLinsert(node);
		}
		else if (g->right == p && p->right == node ) {
			// RR situation
			RRinsert(node);
		}
		else if(g->right ==p && p->left == node){
			// RL situation
			rightrotate(p);
			RRinsert(p);
		}
		else {
			//LR situation
			leftrotate(p);
			LLinsert(p);
		}
	}
}
template<class T> void RBTree<T>::insert(T key) {
	fixColor(BSTinsert(key));
}
/************************Delete basic operation********************************/
template<class T> RBTNode<T>* RBTree<T>::getfar(RBTNode<T>*u) {
	RBTNode<T> * p = u->parent;
	RBTNode<T> * s = nullptr;
	if (u == p->left) {
		s = p->right;
		return s->right;
	}
	else
	{
		s = p->left;
		return s->left;
	}
}
template<class T> RBTNode<T>* RBTree<T>::getnear(RBTNode<T>*u) {
	RBTNode<T> * p = u->parent;
	RBTNode<T> * s = nullptr;
	if (u == p->left) {
		s = p->right;
		return s->left;
	}
	else
	{
		s = p->left;
		return s->right;
	}
}
template<class T> void RBTree<T>::farred(RBTNode<T>*u) {
	RBTNode<T> * p = u->parent;
	RBTNode<T> * s = (u == p->left) ? p->right : p->left;
	RBTNode<T>* far = getfar(u);
	if (far->color == RED)
	{
		s->color = p->color;
		p->color = BLACK;
		far->color = BLACK;
		if (s == p->right) leftrotate(p);
		else rightrotate(p);
	}
}
template<class T> void RBTree<T>::farblack(RBTNode<T>*u) {
	RBTNode<T> * p = u->parent;
	RBTNode<T> * s = nullptr;
	if (u == p->left) s = p->right;
	else s = p->left;
	RBTNode<T>* far = getfar(u);
	RBTNode<T>* near = getnear(u);

	if ((getcolor(far) == BLACK) && (getcolor(near) == RED))
	{
		s->color = RED;
		near->color = BLACK;
		if (s == p->right) rightrotate(s);
		else leftrotate(s);
		farred(u);
	}
}
template<class T> void RBTree<T>::fixdb(RBTNode<T>*u) {	// When u is db
	if (u == root) return;

	RBTNode<T> * p = u->parent;
	RBTNode<T> * s = ((u == p->left) ? p->right : p->left);
	if (getcolor(s) == RED) {
		s->color = BLACK;
		p->color = RED;
		if (s == p->right) leftrotate(p);
		else rightrotate(p);
		// If you have entered the case where s is red, you must refresh sp before entering the case where s is black
	}
	p = u->parent;
	s = ((u == p->left) ? p->right : p->left);

	if (getcolor(s) == BLACK) {
		if ((getcolor(s->left) == BLACK) && (getcolor(s->right) == BLACK)) {
			if (getcolor(p) == RED) {
				p->color = BLACK; s->color = RED;
				return;
			}
			else {
				s->color = RED; fixdb(p);
			}
		}
		else { //s is black, xy has at least one red
			RBTNode<T>* far = getfar(u);
			if (getcolor(far) == RED)
				farred(u);
			else  farblack(u);
		}
	}
}
template<class T> RBTNode<T>* RBTree<T>::getnodeBST(RBTNode<T>*node, T key) {
	// Return the location of the node to be deleted
	if (node == nullptr) return node;
	if (key < node->key) return getnodeBST(node->left, key);
	else if (key > node->key) return getnodeBST(node->right, key);
	else { // Found node to delete

		if (node->left != nullptr) {
			RBTNode<T> * maxleft = getPred(node);
			node->key = maxleft->key;
			return getnodeBST(node->left, maxleft->key);
		}
		else if (node->right != nullptr) {
			RBTNode<T> *minright = getSucc(node);
			node->key = minright->key;
			return getnodeBST(node->right, minright->key);
		}
		else { //If it is a leaf node, then
			return node;
		}
	}
}
template<class T> RBTNode<T>* RBTree<T>::getPred(RBTNode<T>* node) {
	// Return to the largest precursor of node
	RBTNode<T>* maxleft = node->left;
	while (maxleft->right != nullptr) {
		maxleft = maxleft->right;
	}
	return maxleft;
}
template<class T> RBTNode<T>* RBTree<T>::getSucc(RBTNode<T>* node) {
	// Return the minimum successor of node
	RBTNode<T>* minright = node->right;
	while (minright->left != nullptr) {
		minright = minright->left;
	}
	return minright;
}
template<class T> void RBTree<T>::remove(T key) {
	RBTNode<T> * u = getnodeBST(root, key);  //Find the node to be deleted
	if (u == nullptr) {
		std::cout << "Node does not exist" << std::endl;
		return;
	}
	if (u == root) { delete root; root = nullptr; return; }
	else if (u->color == RED)
	{
		if (u == u->parent->left) u->parent->left = nullptr;
		else u->parent->right = nullptr;
	}
	else {
		// When u is db, call db correction function
		fixdb(u);
		if (u == u->parent->left) u->parent->left = nullptr;
		else u->parent->right = nullptr;
	}
	delete u;
	u = nullptr;
	return;
}
/************************Ergodic basic operation********************************/
template <class T> void RBTree<T>::MidOrder() {
	if (root == nullptr) return;
	MidOrder(root);
}
template <class T> void RBTree<T>::MidOrder(RBTNode<T>* node) {
	if (node==nullptr) return;
	MidOrder(node->left);
	std::cout << node->key << ":" << node->color << "   ,"<< "left:";
	if (node->left != nullptr) std::cout << node->left->key;
	std::cout << "," << "right:  ";
	if (node->right!=nullptr) std::cout  << node->right->key;
	std::cout << std::endl;
	MidOrder(node->right);
}

template<class T> void RBTree<T>::clear(RBTNode<T>* node) {
	if (node)
	{
		clear(node->left);
		clear(node->right);
		delete node;
		node = nullptr;
	}
}

main.h

#include<iostream>
#include<time.h>
#include<vector>
#include "RBTree.h"
using namespace std;

int main(int argc, char** argv) {
	srand((int)time(0));
	RBTree<int> tree;
	int times = rand() % 20;
	int copytimes = times;
	vector<int>arr;
	while (copytimes--)
	{
		int val = rand() % 100;
		arr.push_back(val);
		tree.insert(val);
	}
	tree.MidOrder();
	cout << "-------------------------------------" << endl;
	times = 50;
	while (times--)
	{
		int todel = rand() % arr.size();
		cout << "\n try to del " << arr[todel] << endl;
		tree.remove(arr[todel]);
		tree.MidOrder();
		cout << "-------------------------------------" << endl;
	}

	system("pause");
	return 0;
}

reference:

- before AVL deletion, some of the references are lost, some of which are cut from the video

  1. https://blog.csdn.net/FreeeLinux/article/details/52204851 AVL tree delete
  2. Https://www.geeksforgeeks.org/avl-tree-set-1-insertion/avl tree
  3. Https://www.geeksforgeeks.org/insert-operation-in-b-tree/b tree
  4. https://blog.csdn.net/waiwai0331/article/details/51723630 B tree
  5. https://github.com/StudentErick/BTree/blob/master/BTree.h B tree
  6. Https://blog.csdn.net/xuanzhe117/article/details/78039692 B tree
  7. https://blog.csdn.net/u011711997/article/details/80317609 B-tree
  8. https://www.youtube.com/watch?v=GKa_t7fF8o0 B tree deletion demonstration
  9. https://www.cs.usfca.edu/~galles/visualization/BTree.html B tree animation
  10. https://www.youtube.com/watch?v=w5cvkTXY0vQ RBT
  11. http://alrightchiu.github.io/SecondRound/red-black-tree-deleteshan-chu-zi-liao-yu-fixupxiu-zheng.html
  12. https://github.com/anandarao/Red-Black-Tree/blob/master/RBTree.cpp RBT
Published 18 original articles, won praise 16, visited 20000+
Private letter follow

Tags: Database github less

Posted on Fri, 14 Feb 2020 00:51:44 -0500 by kev wood