文章目录
一、基本概念
二叉搜索树(BST)又称二叉排序树,它是一颗空树或者具有以下性质的二叉树:
- 若左树不为空,左树上所有节点的值都小于等于根节点的值
- 若右树不为空,右树上所有节点的值都大于等于根节点的值
- 左右子树也为二叉搜索树

二、二叉搜索树特性
2.1 顺序性
当对二叉搜索树进行中序遍历时,遍历的结构是有序的。
2.2 局限性
二分搜索树在时间性能上是具有局限性的。
在理想情况下,二叉搜索树是“平衡”的,这样就可以在 log𝑛 轮循环内查找任意节点。
然而,如果我们在二叉搜索树中不断地插入和删除节点,可能导致二叉树退化为链表,相应的,二叉搜索树的查找操作是和这棵树的高度相关的,而此时这颗树的高度就是这颗树的节点数 n,这时各种操作的时间复杂度也会退化为 𝑂(𝑛) 。

三、二叉搜索树实现
3.1 插入
插入过程如下:
- 当树为空时,该结点就是root结点
- 树不为空时,大于根结点往右走,小于根结点往左走,直到找到空位置,插入新结点
- 如果要插入相同的值,则进行定义,如:相同的统一往右/左走,直到找到空位置,插入新结点

在这棵树上分别插入2和16这两个结点,会得到如下的树:

代码实现如下:
bool Insert(const K& key)
{
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;
}
3.2 删除
要删除一个结点,首先要保证该结点存在,当查找到结点后,会有以下四种情况:
- 该结点左右孩子均为空
- 该结点左孩子为空,右孩子不为空
- 该结点右孩子为空,左孩子不为空
- 该结点左右孩子均不为空
为了应对这四种情况,有以下四种解决方案:
- 把结点的⽗亲对应孩⼦指针指向空,直接删除结点
- 把结点的父亲对应孩子指针指向右孩子,直接删除结点
- 把结点的父亲对应孩子指针指向左孩子,直接删除结点
- 这种情况下无法直接删除该结点,这时采用的做法是替换法。即:用比它大结点中最小的来换掉它,然后将替换的结点删除。即,右孩子中最左边的结点来替换掉该结点,然后将其删除。

代码实现:
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 (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
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;
}
cur->_key = replace->_key;
if (replaceParent->_left == replace)
replaceParent->_left = replace->_right;
else
replaceParent->_right = replace->_right;
delete replace;
}
return true;
}
}
return false;
}
3.3 查找
查找相对来说很简单,大于该结点往左走,小于该结点往右走,如此进行查找即可
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;
}
四、二叉搜索树key和key/value使用
只有key值的二叉搜索树只支持查找功能,比如:我们要查找某个单词是否在字典中存在,停车场是否存在某辆车等等,但对于其它信息我们无法获取如:单词的意思,车辆停车时长。这样的功能有点单一,而且不支持修改,因为一修改就会破坏其内部结构。所以就有了key/value这种结构,对于每个键值可以赋予一个value,value类型可以是任意对象,这样便可满足对二叉搜索树的修改,因为:key是确定的,我们修改是修改的是对应的value值
这样二叉搜索树便可满足如查询单词,查询车辆的停车时长,单词的引用次数一些简单需求。
对于key/value二叉搜索树的实现与key二叉搜索树类似
五、key/value二叉搜索树
template<class K, class V>
struct BSTNode
{
K _key;
V _value;
BSTNode<K, V>* _left;
BSTNode<K, V>* _right;
BSTNode(const K& key, const V& value)
:_key(key)
, _value(value)
, _left(nullptr)
, _right(nullptr)
{}
};
// Binary Search Tree
// Key/value
template<class K, class V>
class BSTree
{
typedef BSTNode<K> Node;
public:
// 强制生成构造
BSTree() = default;
BSTree(const BSTree& t)
{
_root = Copy(t._root);
}
BSTree& operator=(BSTree tmp)
{
swap(_root, tmp._root);
return *this;
}
~BSTree()
{
Destroy(_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 (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
// 右为空
if (parent->_left == cur)
{
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;
}
cur->_key = replace->_key;
if (replaceParent->_left == replace)
replaceParent->_left = replace->_right;
else
replaceParent->_right = replace->_right;
delete replace;
}
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 Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
private:
Node* _root = nullptr;
};
完!


被折叠的 条评论
为什么被折叠?



