1.二叉搜索树概念
二叉搜索树(Binary Search Tree,BST) 是一种特殊的二叉树,它具有以下性质:
- 每个节点有一个值(通常是数字或者可以比较大小的其他数据类型)。
- 对于每个节点,左子树中的所有节点值都小于该节点的值,右子树中的所有节点值都大于该节点的值。
- 每个节点的左、右子树也都是二叉搜索树。

1.1 二叉搜索树的基本操作:
- 插入:从根节点开始,找到合适的位置(比当前节点小的去左子树,比当前节点大的去右子树),直到空节点处插入新节点。
- 查找:从根节点开始,若目标值小于当前节点值,继续在左子树查找;若目标值大于当前节点值,继续在右子树查找。
- 删除:有三种情况:
- 删除的节点是叶子节点(没有子节点):直接删除。
- 删除的节点有一个子节点:用该子节点替代被删除的节点。
- 删除的节点有两个子节点:用右子树中的最小节点或左子树中的最大节点替代被删除节点。
1.2 性能分析
-
时间复杂度(Time Complexity)
- 最优情况:当树保持平衡时,每个操作的时间复杂度为O(log n),其中n是树中的节点数。
- 最坏情况:如果树退化成一个链表(例如每次插入的值都比当前节点大或者小),那么操作的时间复杂度为O(n)。
- 平均情况:对于随机插入的节点,树的平均高度接近log n,因此操作的时间复杂度通常是O(log n)。
-
空间复杂度(Space Complexity)
二叉搜索树的空间复杂度主要取决于树的高度。对于n个节点的树,空间复杂度为O(n),因为每个节点都需要存储数据和指向左右子树的指针。
2. 二叉搜索树的实现
二叉搜索树的初步实现:
cpp
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
: _left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
~BSTree()
{
Destory(_root);
_root == nullptr;
}
//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root->_key == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
void Destory(Node* root)
{
if (root == nullptr)
{
return;
}
Destory(root->_left);
Destory(root->_right);
delete(root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* copy = new Node(root->_key);
copy->_left = Copy(root->_left);
copy->_right = Copy(root->_right);
return copy;
}
private:
Node* _root = nullptr;
};
2.1二叉搜索树的插入
在二叉搜索树中,每个节点都有一个键值。其插入操作遵循特定的规则:
- 左子树的节点值小于根节点的值;
- 右子树的节点值大于根节点的值。
过程:
-
如果树为空,则直接创建一个新的节点,并将其赋值给根指针
_root。 -
如果树不为空,按照二叉搜索树的规则,若插入的值大于当前节点的值,则向右子树移动;若插入的值小于当前节点的值,则向左子树移动,直到找到一个空位来插入新节点。
-
如果允许插入相等的值,遇到与当前节点值相等的情况时,可以选择将其插入到右子树或左子树。这时,确保插入逻辑的一致性,避免重复值总是被插入到某一侧。
这样,我们保证插入操作能够在符合二叉搜索树规则的同时,保持树的结构合理性。
cpp
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
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;
}

这段代码使用了迭代方式来插入二叉搜索树的节点,避免了递归的深度。在树的查找过程中,迭代地比较当前节点与目标值的大小,决定向左子树还是右子树递归。如果插入位置为空,则创建新的节点并将其连接到父节点。插入操作会检查是否插入重复的节点,如果有相同的键值,则返回 false,避免重复插入,并且插入操作中判断左右子树的顺序不能随意交换。
2.2二叉搜索树的查找
在二叉搜索树中,查找操作是非常高效的,因为树的结构保持了一定的排序规则:左子树所有节点的值小于当前节点的值,右子树所有节点的值大于当前节点的值。
二叉搜索树的查找过程:
-
比较根节点与目标值:
- 如果目标值等于当前节点的值,则查找成功。
- 如果目标值小于当前节点的值,则继续在当前节点的左子树查找。
- 如果目标值大于当前节点的值,则继续在当前节点的右子树查找。
-
时间复杂度:
- 最好的情况是树是完全平衡的,此时查找时间复杂度是 O(log n)。
- 最坏的情况是树退化成链表(例如每次插入的值都比当前节点大或小),此时查找的时间复杂度是 O(n)。
cpp
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
{
cout << "找到了" << endl;
return true;
}
}
cout << "没找到了" << endl;
return false;
}
2.3二叉搜索树的删除
删除过程分析:
-
删除叶子节点 :
如果节点没有左右子树,直接将其父节点指向空,删除该节点。
-
删除只有一个子节点的节点 :
将其父节点的指针指向它的唯一子节点,然后删除该节点。
-
删除有两个子节点的节点 :
需要找到一个替代节点。通常我们选择右子树的最小节点(中序后继)或左子树的最大节点(中序前驱)。然后将该节点的值替换到要删除的节点,并递归删除该替代节点。

cpp
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
{
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->_right;
}
else
{
if (cur = parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
}
}
//两个孩子
else
{
Node* pMinRight = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
pMinRight = minRight;
minRight = minRight->_left;
}
swap(cur->_key, minRight->_key);
if (pMinRight->_left = minRight)
{
pMinRight->_left = minRight->_right;
}
else
{
pMinRight->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
3.key/value搜索
每个关键码(key)都有对应的值(value),其中值(value)可以是任意类型的对象。在树的结构中,每个节点除了存储关键码(key),还需要存储与之对应的值(value)。在进行插入、删除或查找操作时,仍然遵循以关键码(key)为关键字的二叉搜索树规则,可以快速地查找到对应关键码(key)所对应的值(value)。
cpp
template<class K, class V>
struct BSTreeNode
{
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
K _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:
BSTree() = default;
BSTree(const BSTree<K, V>& t)
{
_root = Copy(t._root);
}
BSTree<K, V>& operator=(BSTree<K, V> t)
{
swap(_root, t._root);
return *this;
}
~BSTree()
{
Destory(_root);
_root = nullptr;
}
bool Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
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, value);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
Node* 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 cur;
}
}
return nullptr;
}
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
{
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* pMinRight = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
pMinRight = minRight;
minRight = minRight->_left;
}
swap(cur->_key, minRight->_key);
if (pMinRight->_left == minRight)
{
pMinRight->_left = minRight->_right;
}
else
{
pMinRight->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ":" << root->_value << endl;
_InOrder(root->_right);
}
void Destory(Node* root)
{
if (root == nullptr)
{
return;
}
Destory(root->_left);
Destory(root->_right);
delete root;
}
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* copy = new Node(root->_key, root->_value);
copy->_left = Copy(root->_left);
copy->_right = Copy(root->_right);
return copy;
}
private:
Node* _root = nullptr;
};