目录
1.什么是二叉搜索树
二叉搜索树是一棵具有以下性质的二叉树:
1.若左子树不为空,则左子树上所有节点的值都小于根节点的值。
2.若右子树不为空,则右子树上所有节点的值都大于根节点的值。
2.二叉搜索树的查找
二叉搜索树进行查找时,会先从根节点开始比较:
1.如果查找值小于当前节点值,那么就与当前节点的左子树比较。
2.如果查找值大于当前节点值,那么就与当前节点的右子树比较。
直到查找值等于当前节点值或者当前节点为空为止。
代码实现:
Node* Find(const K& key)
{
if (_root == nullptr) return nullptr;
Node* cur = _root;
while (cur->_key != key)
{
if (key < cur->_key) cur = cur->leftchild;
else if(key > cur->_key) cur = cur->rightchild;
if (cur == nullptr) return nullptr;
}
return cur;
}
其中K是键值类型的模版参数。
3.二叉搜索树的插入
二叉搜索树插入之前会先判断树中是否已有插入的值,如果没有则进行插入。比较方式同上,找到合适的位置,保证二叉搜索树的性质。
代码实现:
bool Insert(const K& key, const V& value)
{
if (Find(key) == nullptr)
{
Node* newnode = new Node(key, value);
if (_root == nullptr) _root = newnode;
else
{
Node* pre = nullptr, *cur = _root;
while (cur != nullptr)
{
pre = cur;
if (key < cur->_key) cur = cur->leftchild;
else cur = cur->rightchild;
}
if (key < pre->_key) pre->leftchild = newnode;
else pre->rightchild = newnode;
}
return true;
}
return false;
}
4.二叉搜索树的删除
二叉搜索树的删除需要分情况讨论:
1.删除节点没有子树:处理父节点与当前节点的关系,即用空指针代替当前节点。删除当前节点。特殊处理根结点。
2.删除节点有1棵子树:处理父节点与当前节点的关系,用当前节点的子树代替当前节点。删除当前节点。特殊处理根结点。
实际上面第1种情况可以并到第2种中。
3.删除节点有2棵子树:找到左子树中的最大值或者右子树中的最小值,处理相关父节点与子节点的关系,用找到的节点代替当前节点。删除当前节点。
代码实现:
bool Erase(const K& key)
{
if (_root == nullptr) return false;
//找到删除节点及其父节点
Node* pre = nullptr, * cur = _root;
while (cur->_key != key)
{
pre = cur;
if (key < cur->_key) cur = cur->leftchild;
else if (key > cur->_key) cur = cur->rightchild;
if (cur == nullptr) return false;
}
//删除节点的子树<=1的情况
if (cur->leftchild == nullptr || cur->rightchild == nullptr)
{
Node* tmp = cur->leftchild;
if (cur->leftchild == nullptr) tmp = cur->rightchild;
if (cur == _root) _root = tmp;
else
{
if (cur->_key < pre->_key) pre->leftchild = tmp;
else pre->rightchild = tmp;
}
}
//删除节点的子树为2
else
{
Node* tmp = cur->leftchild,*t=cur;
while (tmp->rightchild != nullptr)
{
t = tmp;
tmp = tmp->rightchild;
}
swap(tmp->_key, cur->_key);
swap(tmp->_value, cur->_value);
if (t->leftchild == tmp) t->leftchild = tmp->leftchild;
else t->rightchild = tmp->leftchild;
cur = tmp;
}
delete cur;
cur = nullptr;
return true;
}
5.完整代码实现
template<class K, class V>
struct BSTreeNode
{
BSTreeNode(K key=K(),V val=V())
:leftchild(nullptr)
, rightchild(nullptr)
, _key(key)
, _value(val)
{}
BSTreeNode<K,V> * leftchild;
BSTreeNode<K,V> * rightchild;
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)
{
if (Find(key) == nullptr)
{
Node* newnode = new Node(key, value);
if (_root == nullptr) _root = newnode;
else
{
Node* pre = nullptr, *cur = _root;
while (cur != nullptr)
{
pre = cur;
if (key < cur->_key) cur = cur->leftchild;
else cur = cur->rightchild;
}
if (key < pre->_key) pre->leftchild = newnode;
else pre->rightchild = newnode;
}
return true;
}
return false;
}
Node* Find(const K& key)
{
if (_root == nullptr) return nullptr;
Node* cur = _root;
while (cur->_key != key)
{
if (key < cur->_key) cur = cur->leftchild;
else if(key > cur->_key) cur = cur->rightchild;
if (cur == nullptr) return nullptr;
}
return cur;
}
bool Erase(const K& key)
{
if (_root == nullptr) return false;
//找到删除节点及其父节点
Node* pre = nullptr, * cur = _root;
while (cur->_key != key)
{
pre = cur;
if (key < cur->_key) cur = cur->leftchild;
else if (key > cur->_key) cur = cur->rightchild;
if (cur == nullptr) return false;
}
//删除节点的子树<=1的情况
if (cur->leftchild == nullptr || cur->rightchild == nullptr)
{
Node* tmp = cur->leftchild;
if (cur->leftchild == nullptr) tmp = cur->rightchild;
if (cur == _root) _root = tmp;
else
{
if (cur->_key < pre->_key) pre->leftchild = tmp;
else pre->rightchild = tmp;
}
}
//删除节点的子树为2
else
{
Node* tmp = cur->leftchild,*t=cur;
while (tmp->rightchild != nullptr)
{
t = tmp;
tmp = tmp->rightchild;
}
swap(tmp->_key, cur->_key);
swap(tmp->_value, cur->_value);
if (t->leftchild == tmp) t->leftchild = tmp->leftchild;
else t->rightchild = tmp->leftchild;
cur = tmp;
}
delete cur;
cur = nullptr;
return true;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root)
{
if (root == nullptr) return;
_InOrder(root->leftchild);
cout << root->_key << ":" << root->_value << " ";
_InOrder(root->rightchild);
}
private:
Node* _root = nullptr;
};
6.二叉搜索树的应用
1.K模型:只使用键值key,根据key值是否存在解决相关问题。如查找某个单词是否存在的问题。
2.K-V模型:对于每一个key值都有一个value与之对应,根据二者的对应关系解决相关问题。如英文单词与中文的对应问题。
7.二叉搜索树的性能分析
如果二叉搜索树接近完全二叉树或者是完全二叉树,那么搜索的效率为O(logN)。
如果退化为单支树或者接近单支数,那么搜索的效率退化为O(N)。
为了解决这个问题,这里就需要学习红黑树或者AVL树了。