前言介绍
二叉搜索树(Binary Search Tree)顾名思义,是一个为搜索效率而生的数据结构,那接下来我们就先了解二叉搜索树的概念及如何实现。
一、概念
二叉搜索树别名:二叉排序树 / 二叉查找树,它可以是一棵空树,或者是具备以下性质的树:
【非空二叉搜索树必须具备以下性质】
1. 左子树不为空,左子树的所有节点值 < 根结点值
2. 右子树不为空,右子树的所有节点值 > 根结点值
图形实例:(必须严格满足性质)
二、模拟实现
2.1 创造树的节点及树的结构
//思考:创建一个二叉搜索树,首先要构造树的节点,其次再实现树的结构
//创建树的节点时,必须思考节点内部应该包含哪些变量?
// 1. 节点代表的值,2. 节点指向左右子树的指针,3. 指向父亲节点的指针
//开始构建树的节点,我们希望节点内部的成员可以被外界使用,所以使用struct类
template <class V>
struct BSTreeNode
{
BSTreeNode(const V& val)
: _val(val)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
V _val;
BSTreeNode<V>* _left;
BSTreeNode<V>* _right;
BSTreeNode<V>* _parent;
};
//构造完节点,下一步去设计树的结构,我们希望二叉搜索树只对外界展现成员函数,所以使用class类
template <class V>
class BSTree
{
public:
typedef BSTreeNode<V> Node;
//插入
bool insert(const V& val);
//删除
bool erase();
//查找
Node* find(const V& val);
//中序遍历 -> 有序
void inorder();
private:
Node* _root = nullptr; //这里使用指针的原因是动态的增删查改
};
2.2 编写成员函数
(1)插入insert
按照二叉搜索树的性质来插入即可,需要注意的是下面代码不允许插入相同值
bool insert(const V& val)
{
//空树
if(_root == nullptr)
{
_root = new Node(val);
return true;
}
//非空树
else
{
Node* cur = _root;
Node* parent = nullptr;
while(cur)
{
if(cur->_val > val)
{
parent = cur;
cur = cur->_left;
}
else if(cur->_val < val)
{
parent = cur;
cur = cur->_right;
}
else
{
cout << "不允许插入已有的数据" << endl;
return false;
}
}
//已经找到插入节点的父亲节点,开始判断插入左边还是右边
Node* newnode = new Node(val);
// 比根小,去左边插入
if(parent->_val > val)
{
parent->_left = newnode;
}
// 比根大,去右边插入
else
{
parent->_right = newnode;
}
newnode->_parent = parent;
return true;
}
}
(2)查找find
按照二叉搜索树的规则来查找,会发现最坏情况下只需要查找高度次,后面有测效率的代码
Node* find(const V& val)
{
Node* cur = _root;
while(cur)
{
if(cur->_val > val)
{
cur = cur->_left;
}
else if(cur->_val < val)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
(3)中序遍历 inorder
只有中序遍历二叉搜索树才是有序,所以这里只使用中序遍历
void inorder()
{
_inorder(_root);
cout << endl;
}
void _inorder(Node* root) //像这种被封装一层的函数,我们一般不会对外开放,所以要放到private里
{
if(root == nullptr)
return;
_inorder(root->_left);
cout << root->_val << " ";
_inorder(root->_right);
}
(4)删除erase【重点】
在删除节点这里,是会影响二叉搜索树的性质的,所以我们要分下面4种情况来讨论:
情况1:被删除的节点不存在,直接返回false
情况2:被删除的节点存在,但为叶节点,则直接删除,不会影响二叉搜索树
情况3:被删除的节点的只有左孩子
情况4:被删除的节点的只有右孩子
情况2、3、4可以合并解决:
因为我们会发现,情况3、4只是将节点删除,再连接父节点和孩子节点即可,基本对树的结构也无影响,所以情况2、3、4可以合并解决
情况5:被删除的节点有左右孩子
当被删除节点有左右孩子节点的时候,如下图: