博主CSDN主页:all4x的主页
专栏分类:CPP从入门到精通
博主的代码仓库:all4x的仓库
二叉搜索树
1.写在开始前
学到这里,我们就要将c++的学习难度提升一个档次,后续c++高阶的学习主要包括
1.二叉搜索树 2.AVL树 3.红黑树 4.哈希表 5.C++11 6.智能指针等
这部分学习固然困难,但是也是拉开差距的关键时期,我们一起加油!
本章将为大家介绍二叉搜索树,这一章节的学习是为后续对AVL树和红黑树的学习打下基础!
2.二叉树的概念及其性质
二叉树搜索树(BianrySearchTree):又称二叉排序树、二叉查找树。
其性质如下
1.节点值大的在右侧,节点值小的在左侧
2.其左右子树均为二叉搜索树
如下图,就是一个二叉搜索树。大家结合图像来理解。
根据二叉搜索树的性质我们不难发现其中序遍历为有序序列,上图中序遍历结果应为
【1,3,4,6,7,8,10,14,13】
同时,对于一颗二叉搜索树我们仅支持增删查而不支持改,因为一旦修改可能会影响整棵二叉搜索树的平衡。
3.二叉搜索树的模拟实现
还是老样子,对一个数据结构的模拟实现,首先写出基本框架。
对一个树形结构而言,其节点包含左右指针和自身的值,因此实现代码如下。
cpp
template<class K>//模板参数,方便对节点的数据类型进行控制
struct BSTreeNode//定义结点的结构
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)//初始化列表
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
5.二叉搜索树的插入
对于二叉搜索树的插入的原理非常简单,根据二叉搜索树左小右大的性质不难知道,插入值小往左走,插入值大往右走。若树中已经有相等的值,则不插入返回false。
首先找到插入值应该在位置,而后与其前后节点进行链接,实现插入操作。
cpp
bool insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);//对结点Node的空间开辟
return true;
}
Node* cur = _root;//记录当前结点
Node* prev = nullptr;//记录当前结点的前一个结点,以便后续插入的时候进行大小比较
while (cur)//结点的插入
{
if (cur->_key < key)//小值往左走
{
prev = cur;
cur = cur->_right;
}
else if (cur->_key > key)//大值往右走
{
prev = cur;
cur = cur->_left;
}
else//相同的值不需要插入
return false;
}
cur = new Node(key);//先找到位置,再对key进行节点的空间开辟
if (prev->_key > key)//cur与prev间产生连接
{
prev->_left = cur;
}
else
{
prev->_right = cur;
}
return true;
}
6.二叉搜索树的删除分析
删除操作相对于插入的操作稍许复杂。分为四种情况
1.删除的节点没有左右孩子
2.删除的节点只有左孩子
3.删除的节点只有右孩子
4.删除的节点左右节点均存在
下面我们来进行分析。
1.没有左右孩子
直接进行删除即可。
2.有左孩子或右孩子
若是有左节点或有右节点,我们仅需将其的左孩子和右孩子与其父亲链接即可。
代码如下:
cpp
bool Erase(const K& key)//节点的删除
{
Node* prev = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)//要删除的节点比当前节点小,往左边走
{
prev = cur;
cur = cur->_left;
}
else if (cur->_key < key)//要删除的节点比当前节点大,往右边走
{
prev = cur;
cur = cur->_right;
}
else//找到要删除的节点,准备进行删除
{
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == prev->_left)
{
prev->_left == cur->_right;
}
else
{
prev->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else {
if (cur == prev->_left)
{
prev->_left == cur->_left;
}
else if (cur == prev->_right)
{
prev->_right == cur->_left;
}
}
delete cur;
}
4.左孩子与右孩子均存在
若左右孩子均存在,我们这里用到的处理方法叫做替换法。
由于二叉搜索树的性质,为使删除后树的平衡不被破坏,我们删除的节点应被替换合适的值。
那么,什么值是合适的呢。
小于所有右子树的值:即右子树的最小(最左)节点
大于所有左子树的值:即左子树的最大(最右)节点
用图像进行理解
同时我们需要考虑的一个问题是,被替换的节点可能有后续节点。
比如最左节点可能有右子树。
最右节点可能有左子树。
因此在替换后,还需对替换的节点后续节点进行链接操作。
代码如下
cpp
else//左右都不为空
{
Node* prev = cur;
Node* subLeft = cur->_right;
while (subLeft->_left)
{
prev = subLeft;
subLeft = subLeft->_left;
}
swap(cur->_key, subLeft->_key);//交换两个节点的值而不改变连接情况
if (subLeft == prev->_left)
prev->_left = subLeft->_right;//subleft无左节点但可能有右节点
else//subleft为该右子树的第一个节点
{
prev->_right = subLeft->_right;
}
delete subLeft;
}
7.总结
二叉搜索的最常用的操作我们在本篇文章已经介绍完毕,但是既然是树形结构,那么便可以用递归的方式进行模拟实验,大家可以自行尝试。
完整代码如下: