In-depth explanation of <Binary Search Tree> in 20 minutes! ! !

Table of contents

Preamble

First, what is a binary search tree?

1.1 The concept of binary search tree

2. Common operations and implementation of binary search tree

2.1 Search

2.2 insert

 2.3 delete

Three, the application of binary search tree

3.1 K-model

3.2 KV model

Fourth, the performance analysis of the binary search tree

Five, the code

Summarize


Preamble

This article is mainly to lead you to understand the implementation and use of binary search tree

ps: All codes of the binary search tree will be posted at the end of the article (including construction, destruction, assignment, etc.)

First, what is a binary search tree?

1.1 The concept of binary search tree

A binary search tree, also known as a binary sort tree, may be an empty tree or a binary tree with the following characteristics

1. If its left subtree is not empty, all values ​​on its left subtree are smaller than the root

2. If its right subtree is not empty, all values ​​on its right subtree are greater than the root

3. Its left and right subtrees also conform to the binary search tree

As a classic data structure, the binary search tree not only has the characteristics of fast insertion and deletion operations of linked lists, but also has the advantages of fast search of arrays; so it is widely used, for example, it is generally used in file systems and database systems. Data structure for efficient sorting and retrieval operations. 

2. Common operations and implementation of binary search tree

The above figure shows the node structure and member variables of the search binary tree

2.1 Search

Implementation idea:

1. Start the comparison from the root, go to the right if it is bigger than the root, and go to the left if it is smaller than the root

2. Compare the height of the tree at most times, and return false if it is empty and not found

 As shown in the figure above, the left side is non-recursive implementation, and the right side is recursive implementation. Here, the recursive implementation needs to pay attention. Since the member function is called through the *this pointer in the class, and the *this pointer is hidden and cannot control the recursive condition, so in Writing recursion in a class needs to complete the recursive call through subfunctions.


2.2 insert

Implementation idea:

1. If the tree is empty, directly create a new node to the root

2. The tree is not empty, find an empty position through the rules of binary tree search, and insert it

 code show as below

 2.3 delete

Implementation idea:

Use the rules of binary tree search to find the target node, and return false if it does not exist , but the following four situations need to be considered when deleting

1. The target node has no left and right children

2. The target node has a left child but no right child

3. The target node has a right child but no left child

4. There are left and right children of the target node

 Because only the left child or only the right child can handle the situation where there are no left and right children, we merge the processing method that has no left and right children with the processing method that only has left children .

It is handled as follows:

2. Delete the node, and the parent node of the deleted node points to the left child of the deleted node - delete directly

 3. Delete the node, and the parent node of the deleted node points to the right child of the deleted node - delete directly

 4. Find a value that can replace its position, that is, to satisfy that the left subtree is less than this value and the right subtree is greater than this value, we can find the maximum value of the left subtree or the minimum value of the right subtree, and then assign the value to the deleted node into the found maximum or minimum value, and delete the maximum or minimum value node.

 

 As shown in the above picture, the left side is non-recursive writing, and the right side is recursive writing. If you don’t understand, you can private message me

Three, the application of binary search tree

3.1 K-model

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

practical use:

Given a word, determine whether the word is spelled correctly, the specific way:

The specific method is as follows:

1. Use each word in all the word collections in the thesaurus as a key to build a binary search tree
2. Search whether the word exists in the binary search tree, if it exists, the spelling is correct, if it does not exist, the spelling is wrong

3.2 KV model

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

1. For example, the English-Chinese dictionary is the corresponding relationship between English and Chinese . You can quickly find the corresponding Chinese through English. An English word and its corresponding Chinese <word, Chinese> constitute a key-value pair;

2. Another example is to count the number of words . After the statistics are successful, you can quickly find the number of occurrences of a given word. The word and the number of occurrences are <word, count> to form a key-value pair

Fourth, the performance analysis of the binary search tree

Searching in a binary search tree is an unavoidable function. Whether it is insertion or deletion, it must be searched first, so the efficiency of the search is directly linked to the performance of the binary search tree .

 Although it is the same set of sequences, the search binary trees constructed by different insertion orders are also different. As shown in the figure above, the left side is a relatively normal tree, and the right side is a crooked neck tree in extreme cases.

In the optimal case, which is similar to the case on the left, the complexity is the height of the tree, that is: logN

In the worst case, it is similar to the crooked neck tree on the right. At this time, the height of the tree is close to N, so its complexity is: N,

So what is the optimization method for the crooked neck tree on the right?

There must be, and the AVL tree and red-black tree learned in the follow-up will have a great effect

Five, the code

//K模型
namespace key
{
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode(K key)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}

		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;

	};

	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	public:
		//构造函数
		BSTree()
			:_root(nullptr)
		{}
		//拷贝构造函数,用copy函数前序遍历拷贝
		BSTree(BSTree<K>& t)
		{
			_root = copy(t._root);
		}
		Node* copy(Node* root)
		{
			if (root == nullptr)
				return nullptr;

			Node* ret = new Node(root->_key);
			copy(root->_right);
			copy(root->_left);
			return ret;

		}

		//赋值重载
		BSTree<K>& operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}

		//析构函数,调用Destory后续遍历销毁即可
		~BSTree()
		{
			Destory(_root);
		}
		void Destory(Node*& root)
		{
			if (root == nullptr)
				return;

			Destory(root->_right);
			Destory(root->_left);
			delete root;
			root = nullptr;

		}
		 
		//查找
		bool Find(const K& key)
		{
			if (_root == nullptr)
				return false;

			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}
		//递归查找
		bool Findr(const K& key)
		{
			_Findr(_root, key);
		}
		bool _Findr(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key < key)
			{
				return _Findr(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _Findr(root->_left, key);
			}
			else//找到了
			{
				return true;
			}
			return false;
		}

		//删除(erase)
		bool Erase(const K& key)
		{
			if (_root == nullptr)
				return false;

			Node* cur = _root;
			Node* parent = nullptr;
			//找val的位置
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					if (cur->_right == nullptr)//目标位置没有右孩子
					{
						if (cur == _root)//删除根的情况
						{
							_root = cur->_left;
							delete cur;

						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
								delete cur;
							}
							else
							{
								parent->_right = cur->_left;
								delete cur;
							}
						}

					}
					else if (cur->_left == nullptr)
					{

						if (cur == _root)//删除根的情况
						{
							_root = cur->_right;
							delete cur;

						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
								delete cur;
							}
							else
							{
								parent->_right = cur->_right;
								delete cur;
							}
						}

					}
					else
					{
						//我们这里寻找左子树的最大值
						Node* maxLeft = cur->_left;
						Node* pmaxLeft = cur;
						while (maxLeft->_right)
						{
							pmaxLeft = maxLeft;
							maxLeft = maxLeft->_right;
						}
						cur->_key = maxLeft->_key;
						if (pmaxLeft->_left == maxLeft)
						{
							pmaxLeft->_left = maxLeft->_left;
							delete maxLeft;
						}
						else if (pmaxLeft->_right == maxLeft)
						{
							pmaxLeft->_right = maxLeft->_left;
							delete maxLeft;
						}
					}
					return true;
				}
			}
			return false;
		}

		//递归删除
		bool Eraser(const K& key)
		{
			return _Eraser(_root, key);
		}
		bool _Eraser(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key < key)
			{
				return _Eraser(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _Eraser(root->_left, key);
			}
			else
			{
				//找到要删除的位置了
				Node* cur = root;
				if (root->_right == nullptr)
				{
					/*Node* cur = root;*/
					root = root->_left;
					/*delete cur;*/
				}
				else if (root->_left == nullptr)
				{
					/*Node* cur = root;*/
					root = root->_right;
					/*delete cur;*/

				}
				else//左右子树都存在
				{
					Node* pcur = cur;
					cur = cur->_left;

					while (cur->_right)//找左树最大值
					{
						pcur = cur;
						cur = cur->_right;
					}
					root->_key = cur->_key;

					/*if (pcur->_left == cur)
					{
						pcur->_left = cur->_left;
					}
					else if (pcur->_right==cur)
					{
						pcur->_right = cur->_left;
					}*/
					return _Eraser(root->_left, root->_key);

				}
				delete cur;
				return true;
			}
			return false;
		}

		//插入,成功插入返回true,失败也就是已有所要插入的数字则返回false
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
			}
			else
			{
				Node* cur = _root;
				Node* parent = nullptr;
				while (cur)
				{
					//大于,则往右边走
					if (cur->_key < key)
					{
						parent = cur;
						cur = cur->_right;
					}
					else if (cur->_key > key)
					{
						parent = cur;
						cur = cur->_left;
					}
					else
					{
						return false;
					}
				}
				cur = new Node(key);
				if (parent->_key < key)
				{
					parent->_right = cur;

				}
				else
				{
					parent->_left = cur;
				}
			}
			return true;
		}
		//递归插入
		bool Insertr(const K& key)
		{
			return _Insertr(_root, key);
		}
		bool _Insertr(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}

			if (root->_key < key)
			{
				return _Insertr(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _Insertr(root->_left, key);
			}
			return false;
		}


		//中层遍历
		void InOrder()
		{
			_Inorder(_root);
			cout << endl;
		}
		void _Inorder(Node* root)
		{
			if (root == nullptr)
				return;

			_Inorder(root->_left);
			cout << root->_key << " ";
			_Inorder(root->_right);
		}
	private:
		Node* _root = nullptr;
	};
}

Summarize

This article mainly explains the basic concept, implementation and application scenarios of the binary tree, and I hope that the iron people can receive the goods

Guess you like

Origin blog.csdn.net/zcxmjw/article/details/130249378