Table of contents
1.1 Concept of Binary Search Tree
1.2 Binary search tree operation
1.2.1 Search in binary search tree
1.2.2 Insertion of binary search tree
1.2.3 Deletion of binary search tree
2. Implementation of binary search tree
2.2 BST operation member function (non-recursive)
2.3 BST operation member function (recursion)
3. Application of binary search tree
4. Performance Analysis of Binary Search Tree
1. Binary Search Tree (BST)
1.1 Concept of Binary Search Tree
A binary search tree, also known as a binary sort tree, is either an empty tree or a binary tree with the following properties:
- 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
- If its right subtree is not empty, the values of all nodes on the right subtree are greater than the value of the root node
- Its left and right subtrees are also binary search trees
Let me give a few examples to see the structure more intuitively:
1.2 Binary search tree operation
int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};
1.2.1 Search in binary search tree
- Start to compare and search from the root. If it is larger than the root, go to the right to search, and if it is smaller than the root, go to the left to search.
- The height is searched for at most times , and the value does not exist if it is empty and has not been found.
1.2.2 Insertion of binary search tree
The specific process of insertion is as follows:
- If the tree is empty, add a node directly and assign it to the root pointer
- The tree is not empty, find the insertion position according to the nature of the binary search tree, and insert a new node (record the parent node, and judge whether the inserted node should be in the left subtree or the right subtree of the parent node)
1.2.3 Deletion of binary search tree
It seems that there are 4 cases of deleting nodes, but in fact a, b and c can be merged, so there are only 2 cases:
a: The node to be deleted has no children/only one child: delete the node and make the parent node point to the child node of the deleted node (no child is regarded as a child is an empty node, just point to any one)
b: The node to be deleted has left and right children: use the replacement method to find the smallest node in the right subtree of the deleted node (the leftmost node in the right subtree), and replace the value of the smallest node with the value of the deleted node. Then delete the smallest node (at this time, the smallest node either has no children or only one child, and can be deleted directly if it meets the condition a)
2. Implementation of binary search tree
2.1 Basic structure of BST
Node:
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
BST tree:
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
//成员函数
private:
Node* _root=nullptr;
};
Find:
bool Find(const K& key)
{
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;
}
2.2 BST operation member function (non-recursive)
insert:
bool Insert(const K& key)
{
//树为空,则直接新增结点,赋值给_root指针
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
//按性质查找插入的位置,并且记录父结点
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;
}
delete:
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
//查找是否有待删除的节点
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
// 开始删除
// 1、左为空
// 2、右为空
// 3、左右都不为空
if (cur->_left == nullptr)
{
//判断下当前节点是否是_root,若是,无法用parent(当前为nullptr,防止野指针错误)
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
cur = nullptr;
}
else if (cur->_right == nullptr)
{
if (_root == cur)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
cur = nullptr;
}
else
{
//记录删除节点父节点
Node* minParent = cur;
//找到右子树最小节点进行替换
Node* min = cur->_right;
while (min->_left)
{
minParent = min;
min = min->_left;
}
swap(cur->_key, min->_key);
//min在父的左孩子上
if (minParent->_left == min)
//万一最左节点还有右孩子节点,或者是叶子也直接指右为空
minParent->_left = min->_right;
//min在父的右孩子上(待删除节点在根节点,最左节点为根节点的右孩子)
else
minParent->_right = min->_right;
delete min;
min == nullptr;
}
return true;
}
}
return false;
}
Other member functions will not be shown here, here is another small tip:
default: Force the compiler to generate the default structure - the usage of C++11
BSTree()=default;
2.3 BST operation member function (recursion)
It is still recursive, and if you understand the above non-recursive, then you can transform it into recursive.
Find:
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;
}
}
insert:
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);
else
return false;
}
delete:
bool _EraseR(Node* root, const K& key)
{
Node* del = root;
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
{
if (root->_left == nullptr)
root = root->_right;
else if (root->_right == nullptr)
root = root->_left;
else
{
//找右数的最左节点替换删除
Node* min = root->_right;
while (min->_left)
{
min = min->_left;
}
swap(root->_key, min->_key);
//交换后结构改变不是搜索二叉树了,规定范围在右树(因为是右树最左节点替换)再递归
return _EraseR(root->_right, key);
}
delete del;
return true;
}
}
3. Application of binary search tree
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.
2. KV model : Each key code has a corresponding value Value, that is, a key-value pair of <Key, Value>. This methodThe formula is very common in real life:
4. Performance Analysis of Binary Search Tree
Both insertion and deletion operations must be searched first, and the 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 finding each element is equal, 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.
But for the same set of key codes, if the insertion order of each key code is different, a binary search tree with a different structure may be obtained:
In the optimal case : the binary search tree is a complete binary tree (or close to a complete binary tree), and its average number of comparisons is: log(N)
In the worst case : the binary search tree degenerates into a single branch tree (or similar to a single branch), and its average number of comparisons is N
If it degenerates into a single branch tree, the performance of the binary search tree is lost. Can that be improved? No matter what order the key codes are inserted in, can it be optimal? This requires AVL trees and red-black trees.