一、二叉搜索树的概念
⼆叉搜索树 ⼜称**⼆叉排序树**,它或者是⼀棵空树,或者是具有以下性质的⼆叉树:
①左子树不为空,则左子树节点值小于根节点值。
②右子树不为空,则右子树节点值大于根节点值。
③每个子树均为二叉搜索树。
④中序遍历的结果是有序的

一般二叉搜索树不允许插入已有的值,但也有方法可以实现。
后面以二叉树为底层的容器:map,set,multimap,multiset,其中前两个不支持插入相等的值,而后两者便支持。
二、二叉搜索树的实现
节点实现
cpp
template<class K>
struct BSTNode
{
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
BSTNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
1.插入
为了保持其"左小右大"的特性,核心思路就是从根节点开始,寻找到一个合适的空位置进行插入。
如果树为空:直接将新节点作为根节点插入即可。
如果树不为空:从根节点开始,比较待插入节点和当前节点的值,小就插左边,大就插右边,循环找到一个合适的空位进行插入。
注意记录父亲节点的位置

代码实现:
cpp
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key);
if (parent->_key > key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
return true;
}
2.查找
1.从根开始比较,查找的值比根的值大则往右边走查找,反之则往左边走查找。
2.最多查找高度次,走到到空,还没找到,这个值不存在。
3.如果不支持插入相等的值,找到x即可返回
4.如果支持插入相等的值,一般要求查找中序的第一个x。
代码实现:
cpp
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
cur = cur->_left;
}
else if (key > cur->_key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
3.删除(难点)
删除是其中实现难度较大的一点,我们要先查找,并且在删除后要保持二叉树的性质。
1)被删节点的左右孩子均为空
这种情况可直接删除,具体方法:让父亲结点指向空即可,若删除结点在左,那父亲左指向空,反之,父亲右指向空。

2)被删节点有一个孩子节点(左孩子/右孩子)
这种情况本质上就是让孩子节点顶替被删节点

3)被删节点存在两个孩子
这种情况采用替换法,就是用左子树的****最大结点/或者右子树的****最小结点去替换,交换后,在删除。

实现代码:
cpp
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else //找到了要删除的值了
{
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* replaceParent = cur;
Node* replace = cur->_right;
while (replace->_left)
{
replaceParent = replace;
replace = replace->_left;
}
swap(cur->_key, replace->_key);
if (replaceParent->_left == replace)
replaceParent->_left = replace->_right;
else
replaceParent->_right = replace->_right;
delete replace;
}
return true;
}
}
return false;
}
4.中序遍历
InOrder() (公共接口): 这是给用户调用的函数。它内部直接调用私有的 _InOrder 函数,并从根节点 _root 开始遍历。
代码实现:
cpp
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
5.拷贝构造
代码实现:
cpp
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* newroot = new Node(root->_key);
newroot->_left = Copy(root->_left);
newroot->_right = Copy(root->_right);
return newroot;
}
6.析构函数
cpp
~BSTree()
{
Destoy(_root);
_root = nullptr;
}
void Destoy(Node* root)
{
if (root == nullptr)
return;
Destoy(root->_left);
Destoy(root->_right);
delete root;
}
三、二叉搜索树的性能

所以⼆叉搜索树增删查改时间复杂度为:O(N)
如果是一般情况下效率比较高,极端情况下效率就很低了。
以后学习的AVL树和红黑树可以解决这个问题。