- 1. 二叉搜索树的概念
- 2. 二叉搜索树的功能
- 2.1. 二叉搜索树的简单模型
- 2.2. 二叉搜索树的查找
- 2.3. 二叉搜索树的插入
- 2.4. 二叉搜索树的删除
- 3. 二叉搜索树的性能分析
1. 二叉搜索树的概念
二叉搜索树(Binary Search Tree,简称BST)是一种常见的二叉树数据结构,它具有以下特点:
- 每个节点最多有两个子节点,分别称为左子节点和右子节点。
- 对于任意节点,其左子树中的所有节点的值都小于该节点的值,而其右子树中的所有节点的值都大于该节点的值。
- 对于任意节点,其左子树和右子树也分别是二叉搜索树。
这些特性使得二叉搜索树在查找、插入和删除等操作上具有高效性。
简单的二叉搜索树如下图:
2. 二叉搜索树的功能
2.1. 二叉搜索树的简单模型
template<class K, class V>
struct BSTreeNode
{
BSTreeNode(K key, V value)
:_left(nullptr)
,_right(nullptr)
,_key(key)
,_value(value)
{}
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
V _value;
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
bool Insert(const K& key, const V& value)
{}
Node* Find(const K& key)
{}
bool Erase(const K& key)
{}
void _InOrder(Node* root)
{}
void InOrder()
{}
private:
Node* _root = nullptr;
};
2.2. 二叉搜索树的查找
对于二叉搜索树的查找操作,可以通过以下步骤实现:
- 从根节点开始,比较要查找的值与当前节点的值。
- 如果要查找的值等于当前节点的值,则找到了目标节点。
- 如果要查找的值小于当前节点的值,则继续在当前节点的左子树中查找。
- 如果要查找的值大于当前节点的值,则继续在当前节点的右子树中查找。
- 如果在某个节点为nullptr时仍然没有找到目标节点,则说明目标节点不存在于二叉搜索树中。
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
cur = cur->_right;
}
else if (key > cur->_key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
2.3. 二叉搜索树的插入
对于二叉搜索树的插入操作,可以通过以下步骤实现:
- 从根节点开始,比较要插入的值与当前节点的值。
- 如果要插入的值小于当前节点的值,并且当前节点的左子节点为nullptr,则将新节点插入为当前节点的左子节点。
- 如果要插入的值大于当前节点的值,并且当前节点的右子节点为nullptr,则将新节点插入为当前节点的右子节点。
- 如果要插入的值与当前节点的值相等,则不进行插入操作(可以根据需求进行处理)。
- 如果要插入的值与当前节点的值不相等,并且当前节点的左子节点或右子节点不为空,则将当前节点更新为其左子节点或右子节点,然后回到第2步继续比较。
bool Insert(const K& key, const V& value)
{
Node* cur = _root;
Node* parent = _root;
if (cur == nullptr)
{
_root = new Node(key, value);
return true;
}
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
if (key > parent->_key)
{
parent->_right = new Node(key, value);
return true;
}
else if (key < parent->_key)
{
parent->_left = new Node(key, value);
return true;
}
else
{
parent = new Node(key, value);
return true;
}
}
2.4. 二叉搜索树的删除
二叉搜索树的删除操作是一个相对复杂的过程,需要根据被删除节点的子节点情况进行不同的处理。下面是二叉搜索树删除操作的一般步骤:
- 首先,从根节点开始,比较要删除的值与当前节点的值。
- 如果要删除的值小于当前节点的值,则继续在当前节点的左子树中查找要删除的节点。
- 如果要删除的值大于当前节点的值,则继续在当前节点的右子树中查找要删除的节点。
- 如果找到了要删除的节点,需要根据其子节点情况进行不同的处理。
根据被删除节点的子节点情况,可以分为以下三种情况:
-
情况一:被删除节点没有子节点(即为叶子节点)。
-
- 直接删除该节点即可。
-
情况二:被删除节点只有一个子节点。
-
- 将该子节点替代被删除节点的位置。
-
情况三:被删除节点有两个子节点。
-
- 找到被删除节点的右子树中的最小节点(即右子树中最左下方的节点),将其值复制到被删除节点。
- 然后,递归地删除右子树中的最小节点。
下面的示例代码,演示了如何在二叉搜索树种删除节点:
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//找到删除的节点
else
{
//如果是叶子节点
if (cur->_left == nullptr && cur->_right == nullptr)
{
if (cur == _root)
{
delete cur;
_root = nullptr;
return true;
}
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else if (cur == parent->_right)
{
parent->_right = cur->_left;
}
delete cur;
cur = nullptr;
return true;
}
//如果左右节点都不为空
else if(cur->_left != nullptr && cur->_right != nullptr)
{
Node* minRight = cur->_right;
Node* par = cur;
if (minRight == nullptr)
{
_root = cur->_left;
delete par;
par = nullptr;
return true;
}
//找右树的最小值,替换删除的节点
while (minRight->_left)
{
par = minRight;
minRight = minRight->_left;
}
swap(minRight->_key, cur->_key);
swap(minRight->_value, cur->_value);
if (minRight == par->_left)
{
par->_left = minRight->_right;
}
else if (minRight == par->_right)
{
par->_right = minRight->_right;
}
delete minRight;
minRight = nullptr;
return true;
}
//如果右节点为空
else if (cur->_left)
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else if (cur == parent->_right)
{
parent->_right = cur->_left;
}
delete cur;
cur = nullptr;
return true;
}
//如果左节点为空
else if (cur->_right)
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else if (cur == parent->_right)
{
parent->_right = cur->_right;
}
delete cur;
cur = nullptr;
return true;
}
}
}
return false;
}
3. 二叉搜索树的性能分析
二叉搜索树的性能取决于树的平衡性。
**如果二叉搜索树是平衡的,即左右子树的高度差不超过1,那么查找、插入和删除操作的平均时间复杂度将是O(log n)。**这是因为在平衡的二叉搜索树中,每一次操作都可以将搜索范围缩小一半,使得树的高度保持在较小的范围内。
然而,**如果二叉搜索树是不平衡的,即左右子树的高度差较大,那么查找、插入和删除操作的最坏时间复杂度将是O(n)。**这是因为在不平衡的二叉搜索树中,树的高度可能接近于节点数量,导致操作的效率大大降低。
为了解决二叉搜索树的不平衡问题,出现了一些平衡二叉搜索树的变种,比如AVL树、红黑树、B树等。这些树结构通过在插入和删除操作时进行平衡调整,使得树的高度保持在较小的范围内,从而提高了查找、插入和删除操作的性能。
**总结起来,二叉搜索树的性能取决于树的平衡性。在平衡的情况下,二叉搜索树可以提供快速的查找、插入和删除操作,平均时间复杂度为O(log n)。但是,如果树不平衡,性能会下降,最坏时间复杂度为O(n)。**因此,在实际应用中,选择合适的平衡二叉搜索树实现,或者采用其他高效的数据结构,是保证性能的重要考虑因素。
如下图:
查找的时间复杂度为O(log n),但是如果下面的二叉搜索树
查找的时间复杂度将会达到O(n)