Advanced data structure - binary search tree

Table of contents

1. The concept of binary search tree

2. Implementation of binary search tree

Node class

Binary search tree class

2.1 Default member function

2.1.1 Constructor

2.1.2 Copy constructor

2.1.3 Assignment operator overloaded functions

2.1.4 Destructor

2.2 In-order traversal

2.3 insert insert function

2.3.1 Non-recursive implementation

2.3.2 Recursive implementation

2.4 erase delete function

2.4.1 Non-recursive implementation

2.4.2 Recursive version

2.5 find function

2.5.1 Non-recursive implementation

2.5.2 Recursive implementation

3. Application of Binary Search Number

3.1k model

3.2 KV model

4. Binary search tree performance analysis

1. The concept of binary search tree

Binary search tree is also called binary sorting tree . It is either an empty tree or a binary tree with the following properties:

  1. If its left subtree is not empty, the values ​​of all nodes on the left subtree are less than the value of the root node .
  2. If its right subtree is not empty, then the values ​​of all nodes on the right subtree are greater than the value of the root node .
  3. Its left and right subtrees are also binary search trees respectively.

Summary: Any subtree satisfies the value of the left subtree < root < the value of the right subtree.
Binary search tree is also called binary sorting tree, and any subtree satisfies the value of left subtree < root < value of right subtree, so we perform in-order traversal (left subtree root right subtree ) to get is an ascending sequence .

2. Implementation of binary search tree

To implement a binary search tree, two classes must be implemented. One is the node class, which is used to store node values, left pointers, and right pointers. The second class is specially used for addition, deletion, checking and modification of binary search trees.

Node class

The node class mainly contains the following contents:

  1. Member variables: node value, left pointer, right pointer.
  2. Only one constructor is needed to initialize the member variables.
template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left; //左指针
	BSTreeNode<K>* _right;//右指针
	K _key;//节点值
	BSTreeNode(const K& key)//构造函数
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

Binary search tree class

This type is mainly used for additions, deletions, checking and modifications.

  • Basic framework:
template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
    //……
private:
	Node* _root = nullptr;
};

2.1 Default member function

2.1.1 Constructor

The constructor here can be directly generated by the compiler by default, and there is no need to implement it yourself. However, after the subsequent copy constructor is written, the compiler will not generate it by default, but we can force it to generate the constructor by default, but we must use Features of C++11, please see the code:

//强制编译器自己生成构造函数,忽视拷贝构造带来的影响
BSTree() = default;//C++11才支持

2.1.2 Copy constructor

Note that the copy construction here is a deep copy. Here we can directly use pre-order recursion to create a binary tree that is the same as the original one. As for the recursive pre-order copying of nodes, here we can specifically encapsulate a Copy function.

Node* CopyTree(Node* root)
{
	if (root == nullptr)
		return nullptr;
	Node* copyNode = new Node(root->_key);//拷贝根结点
	//递归创建拷贝一棵树
	copyNode->_left = CopyTree(root->_left);//递归拷贝左子树
	copyNode->_right = CopyTree(root->_right);//递归拷贝右子树
	return copyNode;
}
//拷贝构造函数--深拷贝
BSTree(const BSTree<K>& t)
{
	_root = CopyTree(t._root);
}

2.1.3 Assignment operator overloaded functions

Here is the modern writing method: The writing method is very clever. Assume that t2 is assigned to t1. When t2 passes parameters, it directly uses the value-passed parameter to call the copy constructor to generate t . t is a copy of t2. At this time, the swap function is called to exchange t1. And the _root root node of t is enough, and the copy-constructed t will automatically call its own destructor to complete the release after the assignment operator is overloaded.

//赋值运算符重载函数 t1 = t2
BSTree<K>& operator=(BSTree<K> t)//t就是t2的拷贝
{
	//现代写法
	swap(_root, t._root);
	return *this;
}

2.1.4 Destructor

The destructor is to release all the nodes of the binary search tree. Here we give priority to post-order recursive release. You can encapsulate a Destory function specifically for recursively deleting nodes, as follows:

void DestoryTree(Node* root)
{
	if (root == nullptr)
		return;
	//通过递归删除所有结点
	DestoryTree(root->_left);//递归释放左子树中的结点
	DestoryTree(root->_right);//递归释放右子树中的结点
	delete root;
}
//析构函数
~BSTree()	
{
	DestoryTree(_root);//复用此函数进行递归删除结点
	_root = nullptr;
}

2.2 In-order traversal

The core purpose of in-order traversal is left subtree -> root node -> right subtree. Here we use recursion to implement in-order traversal.

  • code show as below:
//中序遍历 -- 递归	
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}
//中序遍历的子树
void _InOrder(Node* root)
{
	if (root == nullptr)
		return;
	_InOrder(root->_left);//递归到左子树
	cout << root->_key << " ";//访问根结点
	_InOrder(root->_right);//递归到右子树
}

2.3 insert insert function

2.3.1 Non-recursive implementation

Combined with the nature of the binary search tree, the implementation of insertion is very simple ( note that repeated values ​​are not allowed to be inserted again, and redundancy is not allowed by default ). Mainly divided into two categories:

1. If it is an empty tree, just use the inserted node as the root node.

2. If the tree is not empty, discuss according to the following rules: first, find the appropriate position of the value to be inserted , and second, after finding the position, link the inserted value to this tree .

  • 1. Find the appropriate location for the value to be inserted.

Define the cur pointer to start from the root node ( the cur pointer is used to find the appropriate position to be inserted ), define the parent pointer to start as nullptr ( the parent pointer is used for the link operation after finding the position ), and position the node value to be inserted at the key . Traverse the cur pointer

  1. If key > the node value pointed to by cur, let the parent go to the position of cur, let the cur pointer go to the right subtree, point to the position of _right, and continue traversing.
  2. If key < the node value pointed to by cur, let the parent go to the position of cur, let the cur pointer go to the left subtree, point to the position of _left, and continue traversing.
  3. If key = the node value pointed to by cur, it means that the node value to be inserted coincides with the current node value of this tree, and the node insertion fails. Return false.

 After the traversal is completed, it means that the appropriate position to be inserted (the end of a certain subtree) has been found, and then points to the second step:

  • 2. Link the inserted value with the parent

The linking steps are very simple, just ensure the link position:

  1. If the inserted value is greater than the parent's value, the link is to the right of the parent.
  2. If the inserted value is smaller than the parent's value, the link is to the left of the parent.

  • code show as below:
//Insert非递归
bool Insert(const K& key)
{
	if (_root == nullptr)//若一开始树为空
	{
		_root = new Node(key);//直接申请值为key的结点作为二叉搜索树的根结点
		return true;
	}
	Node* parent = nullptr;
	Node* cur = _root;
	//1、找寻插入的合适位置
	while (cur)
	{
		if (cur->_key < key)//若key大于当前结点值
		{
			parent = cur;
			cur = cur->_right;//让cur走向右子树
		}
		else if (cur->_key > key)//若key小于当前结点值
		{
			parent = cur;
			cur = cur->_left;//让cur走向左子树
		}
		else
		{
			return false;//若key等于当前结点值,说明插入的值不合法,返回false
		}
	}
	//2、进行与父亲的链接
	cur = new Node(key);
	if (parent->_key < key)
	{
		parent->_right = cur;//比父亲的值大连接在右子树
	}
	else
	{
		parent->_left = cur;//比父亲的值小链接在左子树
	}
	return true;
}
  • Supplement: Searching the binary tree to insert in a relatively ordered manner will be tricky because the height is too high.

2.3.2 Recursive implementation

 It is still divided into two major steps. 1. First recurse to the appropriate position and determine where the inserted value is linked. 2. Find the location and then link.

  • 1. Recursively find the correct position for insertion

Although it is recursive here, the overall idea of ​​finding the correct position is very different from the form of non-recursion:

  1. If key > the node value pointed to by root, let root recurse to the right subtree and continue traversing.
  2. If key < the node value pointed to by root, let root recurse to the left subtree and continue traversing.
  3. If key = the node value pointed to by root, it means that the node value to be inserted coincides with the current node value of this tree, and the node insertion fails. Return false.

 When the root node recurses to nullptr, the next step can be performed: linking.

  • 2. After finding the location, perform the link insertion node

In the previous non-recursive version of the link process, in order to find the link relationship between the newly inserted node and the parent, we specially created a parent pointer, allowing the cur node to update the parent pointer during continuous traversal, so that parent is always the parent of cur. , so that the link relationship is confirmed, but for the recursive implementation here, we do not give a parent pointer, but use a clever method: the parameter is a reference to the pointer!

 From here we can see that the reference passed by the pointer has the effect of not having a parent pointer, which is better than the parent pointer! !

//递归版插入

//插入的子树
bool _InsertR(Node*& root, const K& key)//Node*&为指针的引用
{
	if (root == nullptr)
	{
		root = new Node(key);//当root为空,把自己创建成新结点
		return true;
	}
	if (root->_key < key)
		return _InsertR(root->_right, key);//如果比key小,转换到右子树去插入
	else if (root->_key > key)
		return _InsertR(root->_left, key);//如果比key大,转换到左子树去插入
	else
		return false;//如果相等,就返回false
}

2.4 erase delete function

2.4.1 Non-recursive implementation

The deletion function of the binary search tree is the most complex. Here we mainly perform the deletion operation in two major steps:

  1. Traverse to find the location of the value to be deleted
  2. Delete the found position and link the parent with the remaining nodes

Next, we will discuss these two steps:

  • 1. First find the node to be deleted

First, define the cur pointer to point to the root node (the cur pointer is used to find the location of the node to be deleted), define the parent pointer to point to nullptr (the parent pointer is used for the link operation after deletion), and define key as the value of the deleted node, as follows Rules are traversed:

  1. If key > cur points to the value of the node, let the parent go to the position of cur, and let cur go to the right subtree to traverse.
  2. If key < cur points to the value of the node, let the parent go to the position of cur, and let cur go to the left subtree to traverse.
  3. If key = cur points to the value of the node, then delete the node and link.

 At this point, you can point to the second part, delete the found position and link the father with the remaining nodes.

  • 2. Delete the node and link the parent with the remaining nodes

After I delete the node, one of the most worthy of consideration is what to do if the value to be deleted still has children. Therefore, the problem of linking the father and the child must also be considered, and the following classifications must be made:

  1. The value to be deleted has only one child - the left is empty or the right is empty or both the left and right are empty
  2. Both children of the value to be deleted are present - delete by substitution method

Next is the same discussion:

1. The value to be deleted has only one child - the left is empty or the right is empty or both the left and right are empty

We follow the following four steps:

  1. If the left child is empty and the deleted value is the root node, directly update the root node to the right child (if the right child is empty, do the opposite).
  2. If the father's left child is the value to be deleted, point the father's left child to the right child pointed to by the value to be deleted.
  3. If the father's left child is not the value to be deleted, point the father's right child to the right child pointed to by the value to be deleted.
  4. Delete the node to be deleted.

2. Both children of the value to be deleted are present - delete by substitution method

The purpose of deletion by substitution method is to move one of the leaf nodes of the left subtree or the right subtree to the deleted position after deleting the target node, while maintaining the characteristics of it still being a binary search tree after deletion (left subtree) tree < root < right subtree), this requires the substitution method.

The preparations are as follows:

  1. Define the myParent pointer as the position of the cur pointer (the myParent pointer is used to link the child of the node to be deleted).
  2. Define the minRight pointer as the position of the right child node pointer of cur (minRight is used to find the minimum value of the right subtree).

The specific replacement method is as follows:

  1. Traverse minRight to find the minimum value of the right subtree of the node to be deleted (or the maximum value node of the left subtree), and continuously update myParent in the middle.
  2. After finding it, use the swap function to exchange the value of the minimum node (minRight->_key) and the value of the node to be deleted (cur->_key).
  3. After the exchange, link the parent myParent pointer to the child of the minRight node.
  4. Finally, remember to delete to delete the minRight node.

Note: If the value to be deleted cannot be found after traversing the two major steps of the entire operation, false will be returned directly.

//Erase删除
bool Erase(const K& key)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		//1、先找到要删除的结点
		if (cur->_key < key)
		{
			parent = cur;
			//让parent始终为cur的父亲
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			//让parent始终为cur的父亲
			cur = cur->_left;
		}
		else
		{
			//找到了,分两类情况讨论:
			//1、待删值只有一个孩子 -- 左为空 or 右为空
			//2、待删值两个孩子都在 -- 替换法删除
			if (cur->_left == nullptr)
			{
				if (cur == _root)
				{
					//如果左孩子为空且删除的值为根结点,直接更新根结点为右孩子
					_root = cur->_right;
				}
				else
				{
					//左孩子为空
					if (cur == parent->_left)
					{
						//如果父亲的左孩子为待删值,将父亲的左孩子指向待删值指向的右孩子
						parent->_left = cur->_right;
					}
					else
					{
						//如果父亲的左孩子不是待删值,将父亲的右孩子指向待删值指向的右孩子
						parent->_right = cur->_right;
					}
				}
				//删除待删的结点
				delete cur;
			}
			else if (cur->_right == nullptr)
			{
				if (cur == _root)
				{
					//如果右孩子为空且删除的值为根结点,直接更新根结点为左孩子
					_root = cur->_left;
				}
				else
				{
					//右孩子为空
					if (cur == parent->_left)
					{
						//如果父亲的左孩子为待删值,将父亲的左孩子指向待删值指向的左孩子
						parent->_left = cur->_left;
					}
					else
					{
						//如果父亲的左孩子不是待删值,将父亲的右孩子指向待删值指向的左孩子
						parent->_right = cur->_left;
					}
				}
				//删除待删的结点
				delete cur;
			}
			else
			{
				//待删值的两个孩子都在,替换法删除。
				//找右子树的最小值或找左子树的最大值,下面为找右子树最小值
				Node* minParent = cur;//右子树的根可能就是minRight,所以这里minParent不能为nullptr,
				//因为此时不会进入while循环导致minParent就一直为nullptr,最后删除的时候堆野指针的非法访问
				Node* minRight = cur->_right;
				while (minRight->_left)
				{
					minParent = minRight;
					//让minParent始终为minRight的父亲
					minRight = minRight->_left;
				}
				swap(minRight->_key, cur->_key);//或者cur->_key = minRight->_key;
				//链接父亲minParent和要删除的结点的孩子
				if (minParent->_left == minRight)
				{
					//如果minParent的左孩子为待删值,让minParent的左孩子指向minRight的右
					minParent->_left = minRight->_right;
				}
				else
				{
					//如果minParent的右孩子为待删值,让minParent的右孩子指向minRight的右
					minParent->_right = minRight->_right;
				}
				//删除要删的结点
				delete minRight;
			}
			return true;
		}
	}
	//遍历一遍找不到要删除的值,直接返回false
	return false;
}

2.4.2 Recursive version

This is very different from the main non-recursive implementation idea. It is also divided into two steps: first find the appropriate node location for deletion, and then delete it after finding it and ensure that the link relationship is correct. Next, let’s discuss in detail:

  • 1. First find the node to be deleted:

It is very simple to find the node to be deleted. Non-recursion is through traversal, but recursion is used here to solve the problem:

  1. If the current node root is empty, it means that the deleted node does not exist and returns false.
  2. If key > the node value pointed to by root, let root recurse to the right subtree and continue traversing.
  3. If key < the node value pointed to by root, let root recurse to the left subtree and continue traversing.

 

  • 2. Delete this node + link parent-child relationship:

After deleting a node, we face the same problem as non-recursive deletion: what to do if the value to be deleted still has children, so the problem of linking the father and the child must also be considered, and the following classification must be made:

  1. The value to be deleted has only one child - the left is empty or the right is empty or both the left and right are empty
  2. Both children of the value to be deleted are present - delete by substitution method

The core writing method here is the same as the recursive implementation of insertion. When passing parameters, you need to pass pointer references. Next, in these two deletion situations, I will explain in detail how to make good use of pointer references when passing parameters.

  • 1. The value to be deleted has only one child - the left is empty or the right is empty or both the left and right are empty

We follow the following three steps:

  1. First save the node pointer root to be deleted as del;
  2. If the left child of root is empty, execute root = root->_right; At this time, root is a pointer reference, that is, the left pointer or right pointer of the parent node. It is assumed that root is the right pointer of the parent node. The purpose of executing this code is to link the right child pointer (root) of the parent node to the right child of the root, so that the parent-child link relationship can be naturally established with the reference of the pointer.
  3. If the right child of root is empty, execute root = root->_left;

This situation is no different from the above, except that the link direction has changed, and the idea is the same. Below is a graphic description:

 

  • 2. Both children of the value to be deleted are present - delete by substitution method

The preparations are as follows:

  1. First save the node pointer root to be deleted as del
  2. Define the minRight pointer as the position of the root's right child node pointer (minRight is used to find the minimum value of the right subtree)

The specific replacement method is as follows:

  1. Traverse minRight to find the minimum value of the right subtree of the node to be deleted (or the maximum value node of the left subtree)
  2. After finding it, use the swap function to exchange the value of the minimum node (minRight->_key) and the value of the node to be deleted (root->_key)
  3. After the exchange, reuse recursive deletion in the subtree: return _EraseR(root->_right, key); meaning to use recursive deletion

Illustration description:

 

//递归版删除
bool EraseR(const K& key)
{
	return _EraseR(_root, key);
}
//删除的子树
bool _EraseR(Node*& root, const K& key)
{
	//1、递归查找删除的位置
	if (root == nullptr)
	{
		//如果是空就返回false
		return false;
	}
	if (root->_key < key)
	{
		return _EraseR(root->_right, key);//如果比key小,转换到右子树去插入
	}
	else if (root->_key > key)
	{
		return _EraseR(root->_left, key);//如果比key大,转换到左子树去插入
	}
	//2、确认链接关系
	else
	{
		Node* del = root;//提前保存root结点的位置
		//开始删除
		if (root->_left == nullptr)
		{
			//如果左为空
			root = root->_right;
		}
		else if (root->_right == nullptr)
		{
			//如果右为空
			root = root->_left;
		}
		else
		{
			Node* minRight = root->_right;//minRight用于找到右子树的最小值
			while (minRight->_left)
			{
				minRight = minRight->_left;
			}
			swap(root->_key, minRight->_key);
			return _EraseR(root->_right, key);
		}
		delete del;
		return true;
	}
}

2.5 find function

2.5.1 Non-recursive implementation

The idea of ​​the Find function is very simple. Define the cur pointer to traverse from the root according to the following rules:

  1. If the key value is less than the value of the current node, the search should be performed in the left subtree of the node.
  2. If the key value is greater than the value of the current node, the search should be performed in the right subtree of the node.
  3. If the key value is equal to the value of the current node, the search is successful and true is returned.
  4. If cur traverses a circle and reaches nullptr, it means there is no such node and returns false.
//Find非递归
bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			cur = cur->_right;//若key值大于当前结点的值,则应该在该结点的右子树当中进行查找。
		}
		else if (cur->_key > key)
		{
			cur = cur->_left;//若key值小于当前结点的值,则应该在该结点的左子树当中进行查找。
		}
		else
		{
			return true;//若key值等于当前结点的值,则查找成功,返回true。
		}
	}
	return false;//遍历一圈没找到返回false
}

2.5.2 Recursive implementation

The implementation of recursion is mainly to convert it into sub-problems to solve. For the recursive implementation of Find, just follow the following rules:

  1. If the tree is an empty tree, the search fails and nullptr is returned.
  2. If the key value is less than the value of the current node, recurse to the left subtree of the node to search.
  3. If the key value is greater than the value of the current node, recurse to the right subtree of the node to search.
  4. If the key value is equal to the value of the current node, the search is successful and the address of the corresponding node is returned.
//递归版查找
bool FindR(const K& key)
{
	return _FindR(_root, key);
}
//查找的子树
bool _FindR(Node* root, const K& key)
{
	if (root == nullptr)
		return false;
	if (root->_key < key)
	{
		//如果比key小,转换到右子树去找
		return _FindR(root->_right, key);
	}
	else if (root->_key > key)
	{
		//如果比key大,转换到左子树去找
		return _FindR(root->_left, key);
	}
	else
	{
		//找到了
		return true;
	}
}

3. Application of Binary Search Number

3.1k model

K model: The K model only has key as the key code . Only the Key needs to be stored in the structure , and the key code is the value that needs to be searched .

For example: given a word word, determine whether the word is spelled correctly. The specific method is as follows:

Using each word in the set of all words in the vocabulary as a key, build a binary search tree and
retrieve whether the word exists in the binary search tree. If it exists, it is spelled correctly, if it does not exist, it is spelled incorrectly.
In fact, the binary search tree I simulated earlier is a K model.

3.2 KV model

KV model: Each key has a corresponding value, that is, a key-value pair of <Key, Value>. This method is very common in real life:

For example, the English-Chinese dictionary is the correspondence between English and Chinese. You can quickly find the corresponding Chinese through English. English words and their corresponding Chinese <word, chinese> form a key-value pair; another example is counting the number of words. After the statistics are successful
, Given a word, you can quickly find the number of times it appears. The word and the number of times it appears are <word, count>, which forms a key-value pair.
We can implement the KV model by slightly modifying its internal implementation for the K model.

#pragma once
#include<iostream>
#include<string>
using namespace std;
namespace key_value
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _left; //左指针
		BSTreeNode<K, V>* _right;//右指针
		const K _key;//节点值,假设const修饰防止后续修改key导致破坏二叉搜索树的结构
		V _value;
		BSTreeNode(const K& key, const V& value)//构造函数
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{}
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		//中序遍历 -- 递归
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

		///递归版的插入、删除、查找/
			//查找
		Node* FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		//插入
		bool InsertR(const K& key, const V& value)
		{
			return _InsertR(_root, key, value);
		}
		//删除
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

	private:
		//查找的子树
		Node* _FindR(Node* root, const K& key)//查找的时候要返回结点的指针,为了方便后续达到可以修改value而不能修改key的效果
		{
			if (root == nullptr)
				return nullptr;
			if (root->_key < key)
			{
				//如果比key小,转换到右子树去找
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				//如果比key大,转换到左子树去找
				return _FindR(root->_left, key);
			}
			else
			{
				//找到了
				return root;
			}
		}
		//插入的子树
		bool _InsertR(Node*& root, const K& key, const V& value)//Node*&为指针的引用
		{
			if (root == nullptr)
			{
				root = new Node(key, value);//当root为空,把自己创建成新结点
				return true;
			}
			if (root->_key < key)
				return _InsertR(root->_right, key, value);//如果比key小,转换到右子树去插入
			else if (root->_key > key)
				return _InsertR(root->_left, key, value);//如果比key大,转换到左子树去插入
			else
				return false;//如果相等,就返回false
		}
		//删除的子树
		bool _EraseR(Node*& root, const K& key)
		{
			//1、递归查找删除的位置
			if (root == nullptr)
			{
				//如果是空就返回false
				return false;
			}
			if (root->_key < key)
			{
				return _EraseR(root->_right, key);//如果比key小,转换到右子树去插入
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);//如果比key大,转换到左子树去插入
			}
			//2、确认链接关系
			else
			{
				Node* del = root;//提前保存root结点的位置
				//开始删除
				if (root->_left == nullptr)
				{
					//如果左为空
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
					//如果右为空
					root = root->_left;
				}
				else
				{
					Node* minRight = root->_right;//minRight用于找到右子树的最小值
					while (minRight->_left)
					{
						minRight = minRight->_left;
					}
					swap(root->_key, minRight->_key);
					return _EraseR(root->_right, key);
				}
				delete del;
				return true;
			}
		}

		//中序遍历的子树
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);//递归到左子树
			cout << root->_key << ":" << root->_value << endl;;//访问根结点
			_InOrder(root->_right);//递归到右子树
		}
	private:
		Node* _root = nullptr;
	};
}

4. Binary search tree performance analysis

 Both insertion and deletion operations must be searched first, and search efficiency represents the performance of each operation in the binary search tree. For a binary search tree with n nodes, if the probability of searching for each element is equal, then the average search length of the binary search tree is a function of the depth of the node in the binary search tree, that is, the deeper the node, the more The more times.

However, for the same key set, if each key is inserted in a different order, binary search trees with different structures may be obtained.

  • Under optimal circumstances, the binary search tree is a complete binary tree (or close to a complete binary tree), and its average number of comparisons is: logN.
  • In the worst case, the binary search tree degenerates into a single-branch tree (or similar to a single-branch tree), and its average number of comparisons is: N / 2.
  • In summary, the time complexity is O(N).

Problem: If it degenerates into a single tree, the performance of the binary search tree is lost. Can it be improved so that the performance of the binary search tree can be optimized no matter what order the key codes are inserted? AVL trees and red-black trees can optimize the performance of binary trees.
 

Guess you like

Origin blog.csdn.net/m0_49687898/article/details/131321068