目录
概念
二叉搜索树又称二叉排序树,它的中序遍历序列是一个有序序列。
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
通过二叉搜索树,我们可以初步了解map和set的特性。
二叉搜索树结构
两种结构,一种是K结构,适用于set;另一种是KV结构,适用于map。
本篇讲解KV结构。
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()
:_root(nullptr)
{
}
private:
Node* _root;
}
二叉搜索树操作
查找
要查找的key值大于当前节点值,那就到当前节点的右子树找;key值小于当前节点,就到当前节点的左子树找。
Node* Find(const K& key)
{
if (_root == nullptr)
{
return nullptr;
}
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;
}
递归法:先判断根节点是否是要寻找的,再根据值选择到左子树还是右子树寻找,如果走到空,证明没找到,直接返回空。
Node* _Find_R(Node* root, const K& key)
{
if (root == nullptr)
{
return nullptr;
}
if (root->_key == key)
{
return root;
}
if (root->_key < key)
{
return _Find_R(root->_right, key);
}
else
{
return _Find_R(root->_left, key);
}
}
Node* Find_R(const K& key)
{
return _Find_R(_root, key);
}
插入
插入的前提要先寻找一个空节点,然后根据它与父亲值的大小决定在左还是右插入。
所以我们要寻找空节点时要保存父亲节点。
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;
}
bool _Insert_R(Node*& root, const K& key,const V&value)
{
if (root == nullptr)
{
root = new Node(key,value);
return true;
}
if (root->_key < key)
{
return _Insert_R(root->_right, key);
}
else if (root->_key > key)
{
return _Insert_R(root->_left, key);
}
else
{
return false;
}
}
bool Insert_R(const K& key,const V&value)
{
return _Insert_R(_root, key);
}
删除
删除有有三种情况:
- 要删除节点的左子树为空,让父亲链接它的右子树
- 要删除节点的右子树为空,让父亲链接它的左子树
- 左右子树都不为空,采用替代法删除,在右子树寻找最左节点或左子树寻找最右节点。
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 (parent == nullptr)
{
_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 (parent == nullptr)
{
_root = cur->_left;
}
else if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
}
//删除节点左右都不为空
//替换法删除
else
{
Node* minparent = cur;
Node* min = cur->_right;
while (min->_left)
{
minparent = min;
min = min->_left;
}
swap(cur->_key, min->_key);
swap(cur->_value, min->_value);
if (minparent->_right == min)
{
minparent->_right = min->_right;
}
else
{
minparent->_left = min->_right;
}
delete min;
}
return true;
}
}
//未找到节点
return false;
}
bool _Erase_R(Node*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key > key)
{
return _Erase_R(root->_left, key);
}
else if (root->_key < key)
{
return _Erase_R(root->_right, key);
}
else
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
Node* min = root->_right;
while (min&&min->_left)
{
min = min->_left;
}
swap(root->_key, min->_key);
swap(root->_value, min->_value);
return _Erase_R(root->_right, key);
}
delete del;
return true;
}
}
二叉搜索树性能分析
最好情况下:满二叉树,查找效率为logn
最坏情况下:单枝树,查找效率为n
所以二叉搜索树还是有缺陷的,如何解决缺陷?这就要讲到二叉平衡搜索树,后面章节进行讲解。