个人主页:Lei宝啊
愿所有美好如期而遇
我们简单理解二叉搜索树,二叉搜索树:根的左子树值小于根值,右子树值大于根值,并且所有节点遵循该规则,同时,我们对二叉搜索树进行中序遍历,可以得到从小到大的一个排序,我们举个例子:
接下来我们实现一个二叉搜索树(key,val):
先定义出二叉树的节点,左指针,右指针,键值,val,以及他的构造函数。
template<class T, class V>
struct BSNode
{
struct BSNode* _lhs;
struct BSNode* _rhs;
T _key;
V _val;
BSNode(const T& key, const V& val)
:_key(key)
, _val(val)
,_lhs(nullptr)
,_rhs(nullptr)
{}
};
接下来先写寻找节点的方法,如果要查找的值的等于cur->_val,返回该节点,如果要查找的值大于cur->_val,就去右子树再比较寻找,如果要查找的值小于cur->_val,就去左子树寻找比较,直到找到返回节点或找不到返回空。
Node* Find(const T& val)
{
Node* cur = _root;
while (cur)
{
if (cur->_key == val) return cur;
else if (cur->_key < val) cur = cur->_rhs;
else cur = cur->_lhs;
}
return nullptr;
}
插入节点方法:比较类似于寻找方法,不同的地方在于,寻找是如果找到了,返回true,而我们插入是找到了就返回false,相同的节点插入并没有意义。
这个方法里我们要寻找的是一个空节点,在空节点插入值,而在空节点插入值,我们需要找到他的父节点,所以还要定义一个指针去存父节点。
当我们找到这个空节点以及其他的父节点,判断要插入的值和父节点值的大小,主要是因为如果不比较,是不知道要插入在哪个方向的。
bool Insert(const T& key, const V& val)
{
Node* newnode = new Node(key, val);
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
Node* par = nullptr;
while (cur)
{
par = cur;
if (cur->_key == key) return false;
else if (cur->_key < key) cur = cur->_rhs;
else cur = cur->_lhs;
}
if (par->_key > key) par->_lhs = newnode;
else par->_rhs = newnode;
return true;
}
最后就是删除节点方法,这个需要分情况进行讨论: 我们先画个图
首先还是根据键值先找到节点的值,根据上述三种情况我们分别进行分析。
如果没有找到节点,直接返回flase,没有这个节点当然是删除失败,否则就是找到了节点。
由于要删除节点,我们还是要保存要删除节点的父节点,叶子节点不谈,直接delete就好,如果要删除的节点有一个孩子,我们需要让他的父节点和他的孩子节点连接起来,然后删除该节点。
怎么进行连接呢?我们以删除key值为14的节点为例,10是他的父节点,13是他的左孩子,我们希望10的右指针指向13,也就是14的左孩子。再换种情况:
现在我们要删除1这个节点,我们又希望3的左指针指向2,也就是1的右孩子,也就是说,我们要判断当前要删除的节点是父节点的左孩子还是右孩子,才能断定父节点应该用哪个指针去连接要删除节点的孩子,同时我们还需要判断要删除节点的孩子是左不为空还是右不为空。
事实上,这种情况可以与叶子节点归为一种:
上图:如果孩子都为空,那么par左指针指向nullptr即可
接下来,就是我们最后一种场景:要删除节点左右孩子不为空。
这里我们给出两种删除办法,去要删除节点的右树找最小值,和要删除节点的值进行替换,或者去左树找最大值进行替换,我们这里采取去右子树找最小值。
如果是上图的话,我们可以看到4是3右子树的最小值,替换以后,似乎可以直接删掉叶子节点?我们再看一个图:
还能直接删吗?但是我们又发现,这样不就是要删除的节点有一个孩子了吗?同时我们需要明确的是,右子树找到的最小值,这个最小值节点左子树一定为空,也就是说,这个节点要么没有孩子节点,要么只有右孩子。
值做了交换之后,也就类似一个孩子的解决办法。
特殊情况就是删头节点,我们代码也做了特殊处理。
bool Erase(const T& key)
{
Node* cur = _root;
Node* par = cur;
while (cur)
{
if (cur->_key == key) break;
else if (cur->_key < key)
{
par = cur;
cur = cur->_rhs;
}
else
{
par = cur;
cur = cur->_lhs;
}
}
if (cur == nullptr) return false;
else
{
if (cur->_rhs == nullptr)
{
if (_root == cur)
{
_root = cur->_lhs;
delete cur;
return true;
}
if (par->_lhs == cur)
{
par->_lhs = cur->_lhs;
}
else if (par->_rhs == cur)
{
par->_rhs = cur->_lhs;
}
delete cur;
return true;
}
else if (cur->_lhs == nullptr)
{
if (_root == cur)
{
_root = cur->_rhs;
delete cur;
return true;
}
if (par->_lhs == cur)
{
par->_lhs = cur->_rhs;
}
else if (par->_rhs == cur)
{
par->_rhs = cur->_rhs;
}
delete cur;
return true;
}
else
{
//cur是要删除的位置,替换法变成删rightmin
Node* RightMin = cur->_rhs;
par = cur;
while (RightMin->_lhs)
{
par = RightMin;
RightMin = RightMin->_lhs;
}
cur->_key = RightMin->_key;
//已然到了右树的最左边,也就是说,rightmin左边一定是nullptr
if (par->_lhs == RightMin) par->_lhs = RightMin->_rhs;
else par->_rhs = RightMin->_rhs;
delete RightMin;
return true;
}
}
}