目录
二叉搜索树的概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
3.它的左右子树也分别为二叉搜索树,如下图所示:

二叉搜索树操作
二叉搜索树的查找
二叉搜索树的查找操作其实很简单,若根节点不空,只要比较要查找的值与当前根节点值的大小,根据比较结果去根节点的左子树或右子树中查找就可以了,然后重复上述过程。
二叉搜索树的插入
根据二叉搜索树的性质,找到要插入的位置,将新节点插入即可(如果树为空,直接插入后返回)

二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
要删除的结点无孩子结点
找到该节点,直接删除
要删除的结点只有右孩子结点
找到该节点,同时在寻找的过程中记录其双亲,让其双亲指向待删除的节点的右孩子节点即可。如下图:

要删除的结点只有左孩子结点
找到该节点,同时在寻找的过程中记录其双亲,让其双亲指向待删除的节点的左孩子节点即可。
过程和只存在右孩子节点类似
要删除的结点有左、右孩子结点都存在
不能直接删除该节点,需要分几步完成:
1.找替代节点:在待删除节点的右子树中找最左侧节点,或者在待删除节点左子树中找最右侧节点。
2.将替代节点中的值域赋值给待删除节点。
3.将替代节点删除(注意该替代节点可能还存在左子树或者右子树,在删除时需要考虑其左右子树)
如下图,删除值为5的节点,去其右子树中找最左侧节点,过程如下:

代码实现
首先,节点的结构得有,所以定义如下模板类:
template<class T>
class BTNode{
public:
BTNode<T>* _left;//左指针阈
BTNode<T>* _right;//右指针阈
T _val;//值域
BTNode(T val=T())//使用默认的_val
:_left(nullptr)
,_right(nullptr)
,_val(val)
{
}
};
使用模板类,可以存放任意类型的元素。
然后就可以定义二叉搜索树了,代码如下:
template<class T>
class BSTree{
typedef BTNode<T> Node;//重命名BTNode<T>,方便使用
public:
BSTree()//构造函数,初始化root为空
:_root(nullptr)
{
}
Node* Find(const T& val){//查找元素
Node* cur = _root;
while (cur){
if (cur->_val<val){
cur = cur->_right;
}
else if (cur->_val>val){
cur = cur->_left;
}
else{
return cur;
}
}
return nullptr;
}
Node* insert(T val){//插入元素
if (_root==nullptr){//当前树为空,插入一个元素后直接返回
_root = new Node(val);
return _root;
}
//当前树不空,如下:
Node* parent=nullptr;
Node* child = _root;
while (child){//循环结束后,要插入的节点就是parent的左或者右孩子
parent = child;
if (val<child->_val){
child = child->_left;
}
else{
child = child->_right;
}
}
if (parent->_val>val){//判断要把新节点插入到parent的左还是右
parent->_left = new Node(val);
}
else{
parent->_right = new Node(val);
}
return _root;
}
bool Erase(T val){//删除元素
Node* parent = nullptr;
Node* cur = _root;
while (cur){//寻找待删除节点,注意parent = cur;必须放在if与else if中
if (val<cur->_val){
parent = cur;
cur = cur->_left;
}
else if (val>cur->_val){
parent = cur;
cur = cur->_right;
}
else{
break;
}
}
if (cur==nullptr){//表示没找到,直接返回
return false;
}
//找到了待删除节点,如下:
if (cur->_left == nullptr){//待删除节点只有右孩子,或者待删除节点没有孩子
if (parent == nullptr){//待删除节点是根节点
_root = cur->_right;
delete cur;
}
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;
delete cur;
}
else{//待删除节点不是根节点
if (parent->_left == cur){
parent->_left = cur->_left;
}
else{
parent->_right = cur->_left;
}
delete cur;
}
}
else{//待删除节点左右孩子都有
parent = cur;
Node* rec = cur->_right;
while (rec->_left){//找到其右子树中最左侧节点
parent = rec;
rec = rec->_left;
}
cur->_val = rec->_val;//覆盖待删除节点的值
if (parent->_left==rec){//连接右子树中最左侧节点的右子树
parent->_left = rec->_right;
}
else{
parent->_right = rec->_right;
}
delete rec;//删除掉右子树中最左侧节点
}
return true;
}
~BSTree(){//析构
Destroy(_root);
}
void InOrder(){//暴露给外部,中序遍历
InOrder(_root);
cout << endl;
}
private:
void Destroy(Node*& root){//利用递归销毁二叉树
if (root==nullptr){
return;
}
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
void InOrder(Node*& root){//中序遍历
if (root==nullptr){
return;
}
InOrder(root->_left);
cout << root->_val << ' ';
InOrder(root->_right);
}
private:
Node* _root;//底层维护的指针,指向根节点
};
代码中要注意的点就是当树为空时要插入元素,直接插入返回。当待删除节点是根节点时,需要改变底层维护的指针_root的指向。