欢迎来到Cefler的博客
博客主页:那个传说中的man的主页
个人专栏:题目解析
推荐文章:题目大解析(3)
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
即:左<根<右
bool Insert(const K& key)
{
if (_root == nullptr)//如果为空直接创建新结点
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)//key比当前cur结点的key值小往左边走,大则往右边走,直到遇到空
{
parent = cur;
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);//此时遇到空要创建新结点
//但此时我们还要记得将其与parent连接起来,至于是在parent的左边还是右边,看比大小
if (parent->_key < key)
{
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;//相等说明找到,返回true
}
return false;
}
void _InOrder(Node* root)//中序遍历
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
void InOrder()//套一层
{
_InOrder(_root);
cout << endl;
}
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情
况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
而a情况可以归属于b和c任意一个情况,a情况当把结点删除后,父节点想向指向左右哪边都行,反正都为空无所谓。
替换法删除
:
1.要么从左子树中找到最大结点来替换
2.要么从右子树找到最小结点来替换
所以现在梳理一下代码思路:
1.先找到要删除的结点位置,能找到再删除,找不到返回false
2.开始删除,判情况(b,c,d),对症下药
代码如下:
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)//寻找要删除的结点
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//找到了就可以开始删除了,但是要判情况,对症下药
if (cur->_left == nullptr)
{
//左边为空,则删除后,将父节点连接cur的右边
if (cur == _root)//如果要删除的结点就是初始根结点
{
_root = cur->_right;
}
//先确定此时cur在父节点的左边还是右边
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
else if (cur->_right == nullptr)
{
//右边为空
if (cur == _root)//如果要删除的结点就是初始根结点
{
_root = cur->_left;
}
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
else
{
//左右都不为空,此时用替换法
//这里我们以寻找右边树最小值的替换法执行;而右边树最小值即可以认为就是右边树最左边结点
Node* parent = cur;
Node* subleft = cur->_right;
while (subleft->_left)//当左边遇到空,说明此时subleft已经遍历到最左边
{
parent = subleft;
subleft = subleft->_left;
}
//找到后,可以进行交换了
swap(cur->_key, subleft->_key);
//现在进行删除,而且删除情况属于a情况
if (subleft == parent->_left)
{
parent->_left = subleft->_right;//这边=subleft->_right或者subleft->_left都可以
}
else
{
parent->_right = subleft->_right;
}
}
return true;
}
}
return false;
}
bool _InsertR(Node*& root, const K& key)//递归插入
{
if (root == nullptr)
{
root = new Node(key);//传引用的好处就是此时的root就是其父节点的左/右节点,无需记录父节点
return true;
}
if (root->_key > key)
{
_InsertR(root->_left, key);
}
else if (root->_key < key)
{
_InsertR(root->_right, key);
}
else
return false;
}
bool _FindR(Node* root, const K& key)//递归查找
{
if (root == nullptr)
return false;
if (root->_key > key)
{
FindR(root->_left, key);
}
else if (root->_key < key)
{
FindR(root->_right, key);
}
else
return true;
}
bool _EraseR(Node*& root, const K& key)//这里我们仍然用引用root,这样连接时就不用记录父节点了
{
if (root == nullptr)
return false;
if (root->_key < key)
{
return _EraseR(root->_right, key);
}
else if (root->_key > key)
{
return _EraseR(root->_left, key);
}
else
{
//开始删除
if (root->_left == nullptr)
{
//左为空
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
//左右都不为空
//这里以寻找右子树最小值
Node* subleft = root->_right;
while (subleft->_left)
{
subleft = subleft->_left;
}
swap(root->_key, subleft->_key);
// 转换成在子树去递归删除
return _EraseR(root->_right, key);
}
}
}
#pragma once
#include
using namespace std;
template <class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template <class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
bool Insert(const K& key)
{
if (_root == nullptr)//如果为空直接创建新结点
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)//key比当前cur结点的key值小往左边走,大则往右边走,直到遇到空
{
parent = cur;
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);//此时遇到空要创建新结点
//但此时我们还要记得将其与parent连接起来,至于是在parent的左边还是右边,看比大小
if (parent->_key < key)
{
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;//相等说明找到,返回true
}
return false;
}
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)//寻找要删除的结点
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//找到了就可以开始删除了,但是要判情况,对症下药
if (cur->_left == nullptr)
{
//左边为空,则删除后,将父节点连接cur的右边
if (cur == _root)//如果要删除的结点就是初始根结点
{
_root = cur->_right;
}
//先确定此时cur在父节点的左边还是右边
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
else if (cur->_right == nullptr)
{
//右边为空
if (cur == _root)//如果要删除的结点就是初始根结点
{
_root = cur->_left;
}
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
else
{
//左右都不为空,此时用替换法
//这里我们以寻找右边树最小值的替换法执行;而右边树最小值即可以认为就是右边树最左边结点
Node* parent = cur;
Node* subleft = cur->_right;
while (subleft->_left)//当左边遇到空,说明此时subleft已经遍历到最左边
{
parent = subleft;
subleft = subleft->_left;
}
//找到后,可以进行交换了
swap(cur->_key, subleft->_key);
//现在进行删除,而且删除情况属于a情况
if (subleft == parent->_left)
{
parent->_left = subleft->_right;//这边=subleft->_right或者subleft->_left都可以
}
else
{
parent->_right = subleft->_right;
}
}
return true;
}
}
return false;
}
void InOrder()//套一层
{
_InOrder(_root);
cout << endl;
}
bool FindR(const K& key)
{
return _FindR(_root, key);
}
bool InsertR(const K& key)
{
return _InsertR(_root,key);
}
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
private:
bool _EraseR(Node*& root, const K& key)//这里我们仍然用引用root,这样连接时就不用记录父节点了
{
if (root == nullptr)
return false;
if (root->_key < key)
{
return _EraseR(root->_right, key);
}
else if (root->_key > key)
{
return _EraseR(root->_left, key);
}
else
{
//开始删除
if (root->_left == nullptr)
{
//左为空
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
//左右都不为空
//这里以寻找右子树最小值
Node* subleft = root->_right;
while (subleft->_left)
{
subleft = subleft->_left;
}
swap(root->_key, subleft->_key);
// 转换成在子树去递归删除
return _EraseR(root->_right, key);//root->_right或者root->_left都可以,反正都是空
}
}
}
bool _InsertR(Node*& root, const K& key)//递归插入
{
if (root == nullptr)
{
root = new Node(key);//传引用的好处就是此时的root就是其父节点的左/右节点,无需记录父节点
return true;
}
if (root->_key > key)
{
_InsertR(root->_left, key);
}
else if (root->_key < key)
{
_InsertR(root->_right, key);
}
else
return false;
}
bool _FindR(Node* root, const K& key)//递归查找
{
if (root == nullptr)
return false;
if (root->_key > key)
{
FindR(root->_left, key);
}
else if (root->_key < key)
{
FindR(root->_right, key);
}
else
return true;
}
void _InOrder(Node* root)//中序遍历
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
Node* _root = nullptr;
};
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注❤️ ,学海无涯苦作舟,愿与君一起共勉成长