关于二叉树的基本概念与内容作者在之前的数据结果初阶系列均有讲解,需要的小伙伴可以去作者的往期博客里查看。本篇内容算是对二叉树内容部分的收尾。
一、什么是二叉搜索树
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
二叉搜索数采取中序遍历的顺序,这样就会实现数据从小到大的排列。
二、二叉搜索树的相关操作
1.插入数据
我们先手搓一棵树
接下来我们写一下插入函数(insert),先点一下思路:我们要利用二叉搜索树的性质,我们先用插入的数据与根去比,比根小向左走,比根大向右走,这样它最多比较树的高度次即可完成插入(当然,为了不让数据冗余,如果这棵数中已经有和插入的数据一样大的,那么就选择不插入,所以,我们也应完善它的查找功能)。值得一提的是,这个部分可以用循环写。
bool insert(const K& key)
{
if (_root == nullptr) //如果树是空树直接在根开辟节点即可
{
_root = new node(key);
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);
if (parent->_key < key)//判断插在left还是right
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
bool find(const K & key) //查找
{
node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else //此情况说明树中已经有与插入的相同的数据
{
return true;
}
}
}
2.中序遍历
void inorder(node* root)
{
if (root == nullptr)
{
return;
}
inorder(root->_left);
cout << root->_key << " ";
inorder(root->_right);
}
看起来没什么问题,但如果我们调用这个传参的时候,发现传根节点指针是不行的,因为是私有成员无法访问,直接写一个子函数放在private嵌套即可:
由此可见,二叉搜索树可以提供我们查找,去重,排序的便利。
3.删除数据 (难点)
我们以下面这棵树为例:
我们发现,删除某一个数据分为三类:没有孩子的删除(1、7),一个孩子的删除(6、14),这两种类型的删除比较简单,释放空间然后让其父节点指向其左右孩子构成新的链接即可(一会我们将用代码实现)。真正麻烦的是第三类:有两个孩子节点的删除(3、8),这里直接提供思路,我们要利用二叉搜索树的性质,假设我们要删除3,只需要在以3为根的树中找到一个除了根以外最大的数代替它的位置即可,即左子树的最大,或者是找右子树的最小。将最大(小)值替换到你要删除数据的位置,然后再删除之前的最大(小)值,此时一定满足前两种情况之一。
下面我们用代码完善一下函数
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;
return true;
}
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;
return true;
}
else//第三种情况,2个孩子
{
//右子树中的最小节点
node* rightminp = cur;
node* rightmin = cur->_right;
while (rightmin->_left)//找到右子树中的最小
{
rightminp = rightmin;
rightmin = rightmin->_left;
}
cur->_key = rightmin->_key;//将最小值替换到目标位置
if (rightminp->_left == rightmin)//替换后的删除
rightminp->_left = rightmin->_right;
else
rightminp->_right = rightmin->_right;
delete rightmin;
return true;
}
}
}
return false;//上面情况均未满足,即删除失败
}
以上就是二叉树的加餐内容,二叉搜索树是一个非常重要的内容,它是后续许多结构的底层,作者在后续的讲解的时候还会提到这棵树哦。