搜索二叉树又叫做二叉排序树,它可以是一颗空树,或者是具有以下性质的树
在搜索二叉树中,可以支持插入相同的值,也可以不支持插入相同的值;
在map
/set
/multimap
/multidset
等系列式容器底层就是搜索二叉树,其他map
/set
不支持数据冗余(不支持插入相同的值);multimap
/multiset
支持数据冗余。
这里看一下不支持数据冗余的搜索二叉树:
在最优情况下:搜索二叉树为完全二叉树,其高度是:
log2 N
在最差情况下:搜索二叉树为单支树,其高度为N
综合而言,其时间复杂度为O(N)
而map
和set
底层为AVL树
和红黑树
,并不是简单的搜索二叉树,那样才适用于我们在内存中存储和搜索数据。
补充:
根据搜索二叉树的原理,当我们中序遍历这个二叉树时,这个数据是有序的。
左子树
、根节点
、右子树
(左子树 < 根节点 < 右子树)。
根据搜索二叉树的特点,那在插入的过程中,就要根据插入的值来判断其应该插入到哪里。
代码实现
//二叉搜索树
//节点
template<class T>
struct BSTreeNode
{
T _val;
BSTreeNode* _left;
BSTreeNode* _right;
BSTreeNode(const T& x)
:_key(x)
, _left(nullptr)
, _right(nullptr)
{}
};
//BSTree
template<class T>
class BSTree
{
typedef BSTreeNode<T> Node;
public:
//默认构造
BSTree()
:_root(nullptr)
{}
//插入
bool insert(const T& x)
{
if (_root == nullptr)
{
_root = new Node(x);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (x > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (x < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
Node* newnode = new Node(x);
if (x < parent->_key)
{
parent->_left = newnode;
}
else if (x > parent->_key)
{
parent->_right = newnode;
}
return true;
}
private:
Node* _root;
};
查找根插入有些相似,也是从根节点开始遍历,比节点值大就遍历其右子树;否则遍历其左子树。
true
。false
,表示查找的值不存在。代码实现
//查找
bool find(const T& x)
{
Node* cur = _root;
while (cur)
{
if (x > cur->_key)
{
cur = cur->_right;
}
else if (x < cur->_key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
搜索二叉树的删除就有些复杂了,因为我们删除之后还要保持搜索二叉树的结构。
查找元素是否存在,存在就进行删除,否则返回false
。
删除元素,根据删除元素所在节点的位置可以分为一下四种情况
把删除节点的父节点对于的指针指向
nullptr
,然后删除该节点即可
把删除节点的父节点的对应指针指向删除节点的右孩子节点,然后删除该节点即可。
把删除节点的父节点的对应指针指向删除节点的左孩子节点,然后删除该节点即可。
我们观察这三种情况,处理的方式都有些相似,(像情况一,删除节点的左右孩子都为空,那我们也可以将它归于情况二或者情况三)
现在来看情况四:
现在这种情况,我们就不能单纯的修改指针指向,然后删除了,(如果直接删除,那左右孩子节点就无处可去了);
要删除节点左右孩子节点都不为空
代码实现
//删除
bool erase(const T& x)
{
Node* parent = _root;
Node* cur = _root;
while (cur)
{
if (x > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (x < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
if (cur->_left == nullptr)//左孩子为空或者左右孩子都为空
{
Node* del = cur;
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete del;
}
else if (cur->_right == nullptr) //右孩子为空
{
Node* del = cur;
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete del;
}
else //左右孩子都不为空
{
//在左子树中找最大的
Node* left_max = cur->_left;
Node* left_max_p = cur;
while (left_max->_right)
{
left_max_p = left_max;
left_max = left_max->_right;
}
//赋值
cur->_key = left_max->_key;
//然后删除left_max
if (left_max_p->_left == left_max)
{
left_max_p->_left = left_max->_left;
}
else
{
left_max_p->_right = left_max->_left;
}
delete left_max;
}
return true;
}
}
return false;
}
key
与key/value
使用上述内容,实现的是key
结构是搜索二叉树,还用key/value
结构;
什么意思呢,就是key/value
结构,存储的不单单是key
这一个关键码,还存在一个与关键码有对于关系的元素value
。
key
结构只有
key
作为关键码,只需存在key
即可,key
就是需要搜索到的值,在搜索时只需要判断key
在不在就行了; 在key
场景中不能进行修改,如果修改了就有可能破坏搜索二叉树的结构。
key/value
结构key
都有与之对应的value
,value
可以是任何类型的对象;key
,还需存储value
,增删查改还是以key
为关键字进行;key
/value
的搜索二叉树,支持修改value
但是不支持修改key
。key
/value
结构这里就不实现了,实现起来与key
结构十分相似;