目录
二叉搜索树的概念
二叉树的实现
结点类
函数接口总览
实现二叉树
二叉搜索树的应用
K模型
KV模型
二叉搜索树的性能分析
二叉搜索树(Binary Search Tree,简称BST)是一种特殊的二叉树,其具有以下几个性质:
这些性质确保了在二叉搜索树中进行查找、插入和删除操作具有良好的性能。具体地,这些操作在平均情况下的时间复杂度为 O(logn),其中 n 是树中节点的数量。不过,在最坏情况下(树退化成链表),时间复杂度可能会降为 O(n)。
下面是一个二叉搜索树的示例:
在这个二叉搜索树中:
要实现二叉搜索树,我们首先需要实现一个结点类:
template
struct BSTreeNode
{
K _key; // 结点值
BSTreeNode* _left; // 左指针
BSTreeNode* _right; // 右指针
// 构造函数
BSTreeNode(const K& key = K())
: _key(key)
, _left(nullptr)
, _right(nullptr)
{}
};
//二叉搜索树
template
class BSTree
{
typedef BSTreeNode Node;
public:
//构造函数
BSTree();
//拷贝构造函数
BSTree(const BSTree& t);
//赋值运算符重载函数
BSTree& operator=(BSTree t);
//析构函数
~BSTree();
//插入函数
bool Insert(const K& key);
//删除函数
bool Erase(const K& key);
//查找函数
Node* Find(const K& key);
//中序遍历
void InOrder();
private:
Node* _root; //指向二叉搜索树的根结点
};
为了在实现其他接口的过程中方便随时检查,最好实现一个二叉搜索树的中序遍历接口,当我们对二叉搜索树进行一次操作后,可以调用中序遍历接口对二叉搜索树进行遍历,若二叉搜索树进行操作后的遍历结果仍为升序,则可以初步判断所实现的接口是正确。
//中序遍历的子函数
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left); //遍历左子树
cout << root->_key << " "; //遍历根结点
_InOrder(root->_right); //遍历右子树
}
//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
代码如下:
// 定义二叉搜索树模板类
template
class BSTree
{
private:
BSTreeNode* _root; // 树的根结点
// 辅助函数:递归拷贝树
BSTreeNode* CopyTree(BSTreeNode* root)
{
if (root == nullptr)
return nullptr;
BSTreeNode* newNode = new BSTreeNode(root->_key);
newNode->_left = CopyTree(root->_left);
newNode->_right = CopyTree(root->_right);
return newNode;
}
// 辅助函数:递归销毁树
void DestroyTree(BSTreeNode* root)
{
if (root != nullptr)
{
DestroyTree(root->_left); // 递归销毁左子树
DestroyTree(root->_right); // 递归销毁右子树
delete root; // 删除当前结点
}
}
// 辅助函数:递归插入
BSTreeNode* InsertRecursive(BSTreeNode* root, const K& key)
{
if (root == nullptr)
{
return new BSTreeNode(key); // 找到插入位置后创建新结点
}
if (key < root->_key)
{
root->_left = InsertRecursive(root->_left, key); // 递归插入左子树
}
else if (key > root->_key)
{
root->_right = InsertRecursive(root->_right, key); // 递归插入右子树
}
return root;
}
// 辅助函数:递归删除
BSTreeNode* DeleteRecursive(BSTreeNode* root, const K& key)
{
if (root == nullptr)
return root;
if (key < root->_key)
{
root->_left = DeleteRecursive(root->_left, key); // 在左子树中删除
}
else if (key > root->_key)
{
root->_right = DeleteRecursive(root->_right, key); // 在右子树中删除
}
else
{
if (root->_left == nullptr)
{
BSTreeNode* temp = root->_right;
delete root; // 删除当前结点
return temp;
}
else if (root->_right == nullptr)
{
BSTreeNode* temp = root->_left;
delete root; // 删除当前结点
return temp;
}
BSTreeNode* temp = MinValueNode(root->_right); // 找到右子树中最小值结点
root->_key = temp->_key; // 用右子树中最小值替换当前结点
root->_right = DeleteRecursive(root->_right, temp->_key); // 删除右子树中的最小值结点
}
return root;
}
// 辅助函数:找到最小值结点
BSTreeNode* MinValueNode(BSTreeNode* node)
{
BSTreeNode* current = node;
while (current && current->_left != nullptr)
{
current = current->_left; // 找到最左端的结点即为最小值结点
}
return current;
}
// 辅助函数:递归查找
BSTreeNode* SearchRecursive(BSTreeNode* root, const K& key) const
{
if (root == nullptr || root->_key == key)
return root;
if (key < root->_key)
return SearchRecursive(root->_left, key); // 在左子树中查找
return SearchRecursive(root->_right, key); // 在右子树中查找
}
public:
// 构造函数,初始化空树
BSTree()
: _root(nullptr)
{}
// 拷贝构造函数
BSTree(const BSTree& other)
: _root(nullptr)
{
_root = CopyTree(other._root); // 深拷贝另一棵树
}
// 赋值运算符重载函数
BSTree& operator=(const BSTree& other)
{
if (this != &other)
{
DestroyTree(_root); // 销毁当前树
_root = CopyTree(other._root); // 深拷贝另一棵树
}
return *this;
}
// 析构函数,销毁树
~BSTree()
{
DestroyTree(_root); // 递归销毁树中所有结点
}
// 插入函数(非递归)
void InsertIterative(const K& key)
{
if (_root == nullptr)
{
_root = new BSTreeNode(key); // 插入根结点
return;
}
BSTreeNode* parent = nullptr;
BSTreeNode* current = _root;
while (current != nullptr)
{
parent = current;
if (key < current->_key)
{
current = current->_left; // 移动到左子结点
}
else if (key > current->_key)
{
current = current->_right; // 移动到右子结点
}
else
{
return; // 不插入重复值
}
}
if (key < parent->_key)
{
parent->_left = new BSTreeNode(key); // 插入左子结点
}
else
{
parent->_right = new BSTreeNode(key); // 插入右子结点
}
}
// 插入函数(递归)
void Insert(const K& key)
{
_root = InsertRecursive(_root, key); // 调用递归插入函数
}
// 删除函数(非递归)
void DeleteIterative(const K& key)
{
BSTreeNode* parent = nullptr;
BSTreeNode* current = _root;
while (current != nullptr && current->_key != key)
{
parent = current;
if (key < current->_key)
{
current = current->_left; // 移动到左子结点
}
else
{
current = current->_right; // 移动到右子结点
}
}
if (current == nullptr)
return;
if (current->_left == nullptr || current->_right == nullptr)
{
BSTreeNode* newCurrent;
if (current->_left == nullptr)
{
newCurrent = current->_right;
}
else
{
newCurrent = current->_left;
}
if (parent == nullptr)
{
_root = newCurrent; // 删除根结点
}
else if (current == parent->_left)
{
parent->_left = newCurrent; // 删除左子结点
}
else
{
parent->_right = newCurrent; // 删除右子结点
}
delete current;
}
else
{
BSTreeNode* p = nullptr;
BSTreeNode* temp;
temp = current->_right;
while (temp->_left != nullptr)
{
p = temp;
temp = temp->_left;
}
if (p != nullptr)
{
p->_left = temp->_right;
}
else
{
current->_right = temp->_right;
}
current->_key = temp->_key;
delete temp;
}
}
// 删除函数(递归)
void Delete(const K& key)
{
_root = DeleteRecursive(_root, key); // 调用递归删除函数
}
// 查找函数(非递归)
BSTreeNode* SearchIterative(const K& key) const
{
BSTreeNode* current = _root;
while (current != nullptr && current->_key != key)
{
if (key < current->_key)
{
current = current->_left; // 移动到左子结点
}
else
{
current = current->_right; // 移动到右子结点
}
}
return current; // 返回找到的结点或 nullptr
}
// 查找函数(递归)
BSTreeNode* Search(const K& key) const
{
return SearchRecursive(_root, key); // 调用递归查找函数
}
};
用法和预期效果:
构造函数
BSTree tree;
拷贝构造函数
BSTree tree2 = tree1;
tree1
,创建一个新的二叉搜索树tree2
,其结构和tree1
相同。赋值运算符重载函数
tree2 = tree1;
tree1
到tree2
,覆盖tree2
原来的内容。析构函数
插入函数(非递归)
tree.InsertIterative(10);
10
的结点。插入函数(递归)
tree.Insert(10);
10
的结点。删除函数(非递归)
tree.DeleteIterative(10);
10
的结点。删除函数(递归)
tree.Delete(10);
10
的结点。查找函数(非递归)
BSTreeNode* node = tree.SearchIterative(10);
10
的结点,返回指向该结点的指针,如果未找到则返回nullptr
。查找函数(递归)
BSTreeNode* node = tree.Search(10);
10
的结点,返回指向该结点的指针,如果未找到则返回nullptr
。二叉搜索树(BST)是一种重要的数据结构,广泛应用于各种算法和系统中。以下是一些常见的应用:
K模型是基于二叉搜索树的一种简化形式,主要用于处理单个键(key)的存储和查询。每个结点只包含一个键,不涉及值(value)。
比如:给定一个单词,判断该单词是否拼写正确。具体方式如下:
KV模型是二叉搜索树的扩展形式,用于处理键值对(key-value)的存储和查询。每个结点包含一个键和一个值。
比如:英汉词典就是英文与中文的对应关系,即
以<单词, 中文含义>为键值对,构建一棵二叉搜索树。注意:二叉搜索树需要进行比较,键值对比较时只比较key。
查询英文单词时,只需给出英文单词就可以快速找到与其对应的中文含义。
时间复杂度
查找、插入和删除
遍历
空间复杂度
空间使用
递归调用栈
平衡性
性能优化
总结
二叉搜索树(BST)是一种基础而重要的数据结构,广泛应用于符号表、字典、优先队列和数据库索引等场景。K模型和KV模型分别处理集合和字典的需求。BST的性能在很大程度上取决于树的平衡性,使用自平衡树可以保证最坏情况下的性能。此外,对于特定应用场景,选择合适的数据结构(如B树或哈希表)也非常重要。