二叉搜索树简称为BST树,其主要特点为左孩子数据域的数值 < 父结点数据域的数值 < 右孩子数据域的数值。
如图所示即为一颗二叉排序树:
通过示例可以看出,二叉搜索树实质上就是对于二叉树的推广,只是在普通的二叉树上做出特殊要求罢了。关于二叉树可以参考我的其他博文https://blog.csdn.net/FDk_LCL/article/details/89221363。下面将详细介绍二叉搜索树的增删改查操作。
我们知道二叉搜索树有其特定的结构特点,那么如何插入一个结点、删除一个结点不会影响其结构呢?
首先给出BST树的结构定义:
// BST树的实现
template<typename T>
class BSTree
{
public:
BSTree():_root(nullptr){}
// 非递归实现BST树的插入操作
void noninsert(const T &val); //向BST树中插入结点
void nonLeverOrder(); //层序遍历BST树
void nonremove(const T &val); //然除BST中值为val的结点
private:
// 定义BST树节点的类型
struct BSTNode
{
BSTNode(T data=T()) //零构造
:_data(data)
,_left(nullptr)
,_right(nullptr)
{}
T _data;
BSTNode *_left;
BSTNode *_right;
};
BSTNode *_root; // 指向树的根节点
};
对于插入一个结点,其思想如下:
- 插入第一个结点的时候,首先判断该树是否为nullptr,如果是,则将待插入结点作为根节点,否则循环比较待插入结点的值与树结点的值的大小,找出待插入结点的合适位置,进行插入。(具体过程可根据BST树的特点和如下代码进行分析)
代码实现:
template<typename T>
void BSTree<T>::noninsert(const T &val)
{
if(_root == nullptr)
{
_root = new BSTNode(val);
return;
}
BSTNode *pre = nullptr;
BSTNode *cur = _root;
while(cur != nullptr)
{
pre = cur;
if(val > cur->_data)
{
cur = cur->_right;
}
else if(val < cur->_data)
{
cur = cur->_left;
}
else
{
return;
}
}
if(val > pre->_data)
{
pre->_right = new BSTNode(val);
}
else
{
pre->_left = new BSTNode(val);
}
}
对于删除一个几点,其基本思想为:
- 首先找到待删除结点的位置以及待删除结点父节点的位置;
- 情况一:如果待删除结点有一个孩子结点或者为叶子结点,则只需要让待删除结点指向孩子结点,然后删除待删除结点;
- 情况二:如果待删除结点有两个孩子结点。则需要找出待删除元素的前驱结点,所谓前驱结点指的是当前结点左子树中最大的值,找到前驱结点后,用前驱结点的值覆盖待删除结点的值,此时对前驱结点进行删除。也就回到了最开始谈论的删除只有一个孩子结点或者为叶子结点的情况。(请思考前驱结点可能会有两个结点吗???)
代码实现:
template<typename T>
void BSTree<T>::nonremove(const T &val)
{
// 1. 从_root开始寻找值为val的节点,cur指向它
BSTNode *pre = nullptr;
BSTNode *cur = _root;
while(cur != nullptr)
{
if(val < cur->_data)
{
pre = cur;
cur = cur->_left;
}
else if(val > cur->_data)
{
pre = cur;
cur = cur->_right;
}
else
{
break;
}
}
if(cur == nullptr)
return; //说明该元素不存在于BST树中,直接退出即可
//cur指向待删除元素pre是其父节点
// 2. 先判断是否满足情况二,如果满足,需要找cur的前驱节点,用前驱把cur节点的值给覆盖掉,直接删前驱
if(cur->_left != nullptr && cur->_right != nullptr)
{
BSTNode *old = cur;
pre = cur;
cur = cur->_left;
while(cur->_right != nullptr)
{
pre = cur;
cur = cur->_right;
}
old->_data = cur->_data;
}
// 3. 删除情况一 直接删除cur指针指向的节点就可以了
BSTNode *child = cur->_left;
if(child == nullptr)
{
child = cur->_right;
}
if(pre == nullptr) // cur指向的根节点
{
_root = child;
}
else
{
// 要把删除节点的孩子赋给cur父节点相应的地址域里面
if(cur == pre->_left)
{
pre->_left = child;
}
else
{
pre->_right = child;
}
}
delete cur;
}