文章目录
- 前言
- 一、二叉搜索树的概念
- 二、二叉搜索树的实现
- 1.二叉树的结点定义
- 2.二叉搜索树的结构
- 3.二叉搜索树的构造函数
- 4.二叉搜索树的插入(非递归)
- 5.二叉搜索树的中序遍历(排序)
- 6.二叉搜索树的查找(非递归)
- 7.二叉搜索树的删除(非递归)
- 8.二叉搜索树的查找(递归)
- 9.二叉搜索树的插入(递归)
- 10.二叉搜索树的删除(递归)
- 11.二叉搜索树的销毁
- 12.拷贝构造函数
- 13.赋值运算符重载
- 三、二叉搜索树的实现完整代码
- 总结
前言
在前面我们其实已经分析过二叉树了,所谓二叉树就是度为2的树。而二叉树中,又有一种特殊的树称之为二叉搜索树
一、二叉搜索树的概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
为什么我们将这样的树称作二叉搜索树呢?因为这样的树天生就十分适合搜索。是想一下,使用上图中最右边的树,如果我们想找到7的话,首先7比6大,所以一定在右子树,而7小于9,所以一定在左子树。结果发现就找到了。从而我们可以发现,这种树的效率天生就很高。
而且这棵树最多找高度次。也就是说最多找N次(注意不是logN次,因为我们可能碰到下图中右边的树),下图中的两棵树都是二叉搜素树。也就是说时间复杂度为O(N)
普通的二叉搜索树的时间复杂度为O(N),但是我们可以对其进行一些特殊变换,使之成为AVL树,红黑树。这样时间复杂度就可以降为了O(logN)了。
这棵树还有一个名字是二叉排序树,这是因为当我们对这棵树进行中序遍历的时候,结果是升序的。
二、二叉搜索树的实现
1.二叉树的结点定义
对于二叉树的结点,我们类似于list的结点定义一样,使用一个struct类。在这个类中,我们可以有默认构造函数,这个默认构造函数的缺省值为K()。
template<class K>
struct BSTreeNode
{
BSTreeNode(const K& val = K())
:_key(val)
,_left(nullptr)
,_right(nullptr)
{}
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
};
2.二叉搜索树的结构
为了方便我们后序的结点的定义,我们可以先将结点给typedef一下。然后我们的成员变量其实也就这一个根节点。只要有根节点就可以构建出二叉搜索树了。
private:
typedef BSTreeNode<K> Node;
Node* _root;
3.二叉搜索树的构造函数
public:
BSTree()
:_root(nullptr)
{}
如上代码所示,对于构造函数其实是比较简答的,我们只需要将二叉树的根结点初始化为空即可
4.二叉搜索树的插入(非递归)
对于插入接口,我们需要考虑的情况有如下几种:
- 二叉树为空的时候,直接new一个结点插入上去即可
- 如果二叉树不为空,那么我们就需要进行比较
- 如果我们想要插入的值大于当前结点的值,那么我们就将当前结点迭代到右孩子
- 如果我们想要插入的值小于当前结点的值,那么我们将当前结点迭代到左孩子
- 如果恰好等于当前结点的值,那么我们可以认为插入失败了,因为此时已经不满足二叉搜索树的定义了。
- 最终我们会发现如此迭代下去,最终的当前结点一定会为空指针,这并非我们所期望的。我们肯定是还需要它的父节点的,这样才能让我目标值插入进去。所以我们还需要一个值来记录当前所迭代位置的父节点。即我们下面代码的parent
- 有了parent以后,我们现在就是new一个新的结点,然后直接parent链接起来。不过此时又分为两种情形,是左孩子还是右孩子,其实并不好确定。于是我们可以使用parent当前的值与目标值进行比对从而判断出是插入左孩子还是右孩子。
public:
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
else
{
Node* parent = _root;
Node* cur = _root;
while (cur != nullptr)
{
if (cur->_key == key)
{
return false;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
parent = cur;
cur = cur->_right;
}
}
cur = new Node(key);
if (parent->_key > key)
{
parent->_left = cur;
}
else if (parent->_key < key)
{
parent->_right = cur;
}
return true;
}
}
5.二叉搜索树的中序遍历(排序)
插入了以后,我们可以使用中序遍历对其进行输出,由于中序遍历就是排序后的情形,所以我们可以很方便的看到我们当前二叉树的结果。
对于排序,我们最好写一个子函数来实现,因为没有子函数的话,参数不好进行控制,会使得代码十分繁琐。
如下代码所示,对于中序遍历我们其实还是比较容易看懂的。无非就是先左子树,然后根,然后右子树即可。我们最好将子函数放在私有的部分中,因为在类外我们并不需要直接使用这个函数
public:
void InOrder()
{
_InOrder(_root);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
6.二叉搜索树的查找(非递归)
对于查找的逻辑,是非常简单的,它与插入也是比较相似的,当我们当前结点的值小于目标的时候,迭代到右子树,当大于目标的时候,迭代到左子树。当相等的时候返回true即可。但若当cur都为nullptr了的时候还没有找到,那就是没有找到,返回false即可
public:
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key == key)
{
return true;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
}
return false;
}
7.二叉搜索树的删除(非递归)
对于二叉搜索树的删除操作,其实是比较难的。这里出现了至少十种以上的情形。
-
找目标结点。这里的找目标结点,我们采用与插入法类似的操作,有一个当前结点和一个父结点。一开始自然是cur为根节点,parent为nullptr。然后我们进行迭代, 当cur不为空的时候,如果当前cur里面的值大于目标值,则parent迭代为cur,cur迭代为左孩子。如果cur里面的值小于目标值的话,则parent迭代为cur,cur迭代为右孩子。如果相等的情况下,由于比较复杂,我们在后面在进行讨论。如果迭代下去。只要没有出现相等的情形,迟早会离开循环,离开循环了,那么直接返回false即可
-
如果中间遇到相等的情况,也就是说,我们已经找到了目前要删除的值,这个值就是cur所指向的结点。这个时候,我们需要进一步将其进行分析。
-
这里存在的主要问题就是cur与parent以及cur的孩子之间的关系比较复杂。
-
首先cur的孩子就分为四种情况,分别是没有孩子、只有左孩子、只有右孩子、两个孩子都有。由于我们要删除cur结点,所以孩子的情况的不同,我们所需要的处理方式就不一样。
-
如果没有孩子的话,那么其实我们只需要直接删除该结点,然后将parent所指向cur的位置给置空即可。如下图所示:即当我们想要在下面这棵树中删除13的时候的情形
-
如果cur只有一个左孩子,也就是说右孩子为空。那么我们可以,使用托孤的方式,即将cur的左孩子直接与parent连接起来,然后在直接删除cur即可
-
如果cur只有一个右孩子,也就是说,左孩子为空,那么我们可以和上面一样采用类似的手段,托孤,将 cur的右孩子直接与parent进行连接,然后删除cur
-
如果两个孩子都存在的话,那么此时情形比较复杂,我们采用替代法。替代法的基本思路是,将要删除的该结点视为一棵子树,然后找到这棵树的左子树的最大值,或者右子树的最小值。然后我们与这两个中的任何一个进行替换即可,然后删除原来被替换的结点。如下图所示、
其实关于以上的cur与其孩子的四种情形,可以简化为三种情况:即将没有孩子归为只有一个左孩子或者只有一个右孩子的情形,因为我们可以将他们的孩子视为一个nullptr,如果这样思考的话,那么前三种方案都是使用托孤的思路。唯独第四种,我们需要使用替代法进行解决
-
-
除了cur孩子之间的问题以外,还有parent和cur之间的关系也是存在一些情况的。
-
cur为parent的左孩子或右孩子,这种情形是比较简单的,也是前面图中的情形。我们只需要与cur与它的孩子的情况一关联即可。其实就是前面cur与其孩子的变化。
-
parent不存在,即cur就是根节点,这种情况我们需要进行特殊处理,因为此时已经不存在父节点了
① 当cur的左孩子或者右孩子任意一个为空的时候,此时我们采用托孤的处理方案,不过此时的托孤需要一些变化,因为cur其实就是_root,所以我们直接让_root赋值为cur->_left或者cur->_right即可。
②当cur的左孩子和右孩子都存在的时候,我们还是使用替代法
如下是我们正常时候的替代,变化还不是很大。
但是当恰好leftMax就是cur的左侧结点的时候,这时候就出现意外了,因为我们是需要进行连接的,所以需要知道leftMax的父节点是谁才可以方便连接,上图是因为leftMax肯定是它的右孩子,而此时变成了左孩子,所以需要特殊处理。(这里的特殊处理在前面的每一次使用替代法中都会出现这种情况)
-
-
根据以上的分析,我们已经可以写出以下的删除代码了:
由于这里存在三代人之间的关系,即parent与cur之间的关系可分为三种情况,cur与其孩子的关系可分为三种情况,这里可以不严格的要求先考哪一类情况。我们两种代码都给出
以下是先考虑parent与cur这两代人之间的关系所写的代码
public:
bool Erase1(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 (parent == nullptr)
{
if (cur->_left == nullptr)
{
_root = cur->_right;
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
_root = cur->_left;
delete cur;
return true;
}
else
{
Node* leftMaxParent = cur;
Node* leftMax = cur->_left;
if (leftMax->_right == nullptr)
{
leftMax->_right = cur->_right;
delete cur;
_root = leftMax;
return true;
}
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, cur->_key);
leftMaxParent->_right = leftMax->_left;
delete leftMax;
leftMax = nullptr;
return true;
}
}
if (parent->_left == cur)
{
if (cur->_left == nullptr)
{
parent->_left = cur->_right;
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
parent->_left = cur->_left;
delete cur;
return true;
}
else
{
Node* leftMaxParent = cur;
Node* leftMax = cur->_left;
if (leftMax->_right == nullptr)
{
leftMax->_right = cur->_right;
delete cur;
parent->_left = leftMax;
return true;
}
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, cur->_key);
leftMaxParent->_right = leftMax->_left;
delete leftMax;
leftMax = nullptr;
return true;
}
}
else
{
if (cur->_left == nullptr)
{
parent->_right = cur->_right;
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
parent->_right = cur->_left;
delete cur;
return true;
}
else
{
Node* leftMaxParent = cur;
Node* leftMax = cur->_left;
if (leftMax->_right == nullptr)
{
leftMax->_right = cur->_right;
delete cur;
parent->_right = leftMax;
return true;
}
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, cur->_key);
leftMaxParent->_right = leftMax->_left;
delete leftMax;
leftMax = nullptr;
return true;
}
}
}
}
return false;
}
以下是根据先考虑cur与其孩子之间的关系所写的代码:
public:
bool Erase2(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)
{
if (parent == nullptr)
{
_root = cur->_right;
}
else if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else if (parent->_right == cur)
{
parent->_right = cur->_right;
}
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else if (parent->_right = cur)
{
parent->_right = cur->_left;
}
}
else
{
Node* leftMax = cur->_left;
Node* leftMaxParent = cur;
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(cur->_key, leftMax->_key);
if (leftMaxParent->_left == leftMax)
{
leftMaxParent->_left = leftMax->_left;
}
else
{
leftMaxParent->_right = leftMax->_left;
}
cur = leftMax;
}
delete cur;
return true;
}
}
}
可见,虽然无论先写哪一种情形,其实都可以完成这个接口,但是代码量有显著的差异。这里推荐先考虑cur和其后代之间的关系。因为替代法这个出现于cur与其后代之间的关系之中。
8.二叉搜索树的查找(递归)
二叉树其实本身还是比较适合递归的。这里的思路也很简单, 不过我们为了可以控制参数,我们必须要写一个子函数去解决,当根节点为空的时候,直接返回false即可,就是没找到,当当前的结点小于要查找的结点,那么去右子树找,当大于时候,去左子树找即可。
public:
bool FindR(const K& key)
{
return _FindR(_root, key);
}
private:
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key == key)
{
return true;
}
else if (root->_key > key)
{
return _FindR(root->_left, key);
}
else
{
return _FindR(root->_right, key);
}
}
9.二叉搜索树的插入(递归)
这里的递归插入也是比较简单的,我们仍然使用子函数调用,当小于待插入的值的时候则去右子树插入,当大于待插入的值的时候,去左子树插入,但是如果遇到相等了,那么就只能插入失败了。最终我们一定会遇到一个空树,我们可以直接new一个结点来,不过这里的问题是如何连接?其实我们可以使用引用,使用引用的话,此时的root本身就早已跟父节点连接到一块了,所以我们直接给这个结点进行赋值,即可解决问题。
public:
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
private:
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (root->_key < key)
{
return _InsertR(root->_right, key);
}
else if (root->_key > key)
{
return _InsertR(root->_left, key);
}
else
{
return false;
}
}
10.二叉搜索树的删除(递归)
如下是递归式删除的操作。在这里我们还是使用子函数调用方便控制参数。这里最好使用引用,因为引用的话永远都是这一棵树。比较容易操作,否则就得传入二级指针就比较麻烦了。
如果root为空,就代表这棵树里面根本就没有key,自然直接返回nullptr即可。
如果当前结点的值小于key,那么去右子树删除,如果大于,去左子树删除。
当等于的时候,我们可以这样操作:先将待删除的结点给记录下来,如果该节点左孩子为空,或者右孩子为空的话,我们可以直接覆盖的方式去迭代。这步操作相对而言是比较妙的。
那么我们不妨思考一下:前面的非递归形式,能否使用这种方式来进行迭代呢?其实是不可以的,这里能迭代是因为引用,永远都是那个空间,且由于在不同的栈帧原因导致了引用的指向被巧妙的改变了。而前者他们并没有使用引用,都是使用局部变量来进行迭代的,因为C++中的引用是不可以改变指向的。所以自然不可以。
如下图所示,非递归中,迭代的本质,仅仅只是局部变量的不断更替,不会直接影响root,这里局部变量的修改仅仅只是间接修改堆区已有数据的连接关系。如果直接赋值的话,只是修改了栈区的局部变量的数据,并未修改实际关系。
如果cur的两个孩子都存在的话,我们还是使用替代法,设置一个局部变量leftMax,让其指向cur->_left,然后不断迭代他,找到这颗树的左子树最大结点。然后交换结点,最后我们知道要删除的结点一定在左子树,所以我们使用递归在左子树中找到要删除的结点即可。
注意这里千万不要将leftMax给传入了。这样做看似可以,但是leftMax是一个局部变量,等到离开本层的递归的时候,leftMax被断了,整棵树的逻辑关系全部混乱了。
public:
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
private:
bool _EraseR(Node*& root, const K& key)
{
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
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
Node* leftMax = root->_left;
while (leftMax->_right)
{
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, root->_key);
return _EraseR(root->_left, key);
}
delete del;
return true;
}
}
11.二叉搜索树的销毁
销毁操作比较简答, 直接按照后序的方式进行销毁即可。最好传引用,因为可以置空root指针
public:
~BSTree()
{
_Destory(_root);
}
private:
void _Destory(Node*& root)
{
if (root == nullptr)
{
return;
}
_Destory(root->_left);
_Destory(root->_right);
delete root;
root == nullptr;
}
12.拷贝构造函数
拷贝构造函数,我们需要实现一个深拷贝,这里我们使用先序的方式进行构造。
public:
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
private:
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* Copyroot = new Node(root->_key);
Copyroot->_left = Copy(root->_left);
Copyroot->_right = Copy(root->_right);
return Copyroot;
}
13.赋值运算符重载
赋值运算符重载,我们可以使用现代写法,这样做是最方便的
public:
BSTree<K>& operator=(BSTree<K> t)
{
std::swap(_root, t._root);
return *this;
}
三、二叉搜索树的实现完整代码
#pragma once
template<class K>
struct BSTreeNode
{
BSTreeNode(const K& val = K())
:_key(val)
,_left(nullptr)
,_right(nullptr)
{}
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
BSTree()
:_root(nullptr)
{}
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
~BSTree()
{
_Destory(_root);
}
BSTree<K>& operator=(BSTree<K> t)
{
std::swap(_root, t._root);
return *this;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
else
{
Node* parent = _root;
Node* cur = _root;
while (cur != nullptr)
{
if (cur->_key == key)
{
return false;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
parent = cur;
cur = cur->_right;
}
}
cur = new Node(key);
if (parent->_key > key)
{
parent->_left = cur;
}
else if (parent->_key < key)
{
parent->_right = cur;
}
return true;
}
}
void InOrder()
{
_InOrder(_root);
}
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key == key)
{
return true;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
}
return false;
}
bool Erase1(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 (parent == nullptr)
{
if (cur->_left == nullptr)
{
_root = cur->_right;
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
_root = cur->_left;
delete cur;
return true;
}
else
{
Node* leftMaxParent = cur;
Node* leftMax = cur->_left;
if (leftMax->_right == nullptr)
{
leftMax->_right = cur->_right;
delete cur;
_root = leftMax;
return true;
}
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, cur->_key);
leftMaxParent->_right = leftMax->_left;
delete leftMax;
leftMax = nullptr;
return true;
}
}
if (parent->_left == cur)
{
if (cur->_left == nullptr)
{
parent->_left = cur->_right;
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
parent->_left = cur->_left;
delete cur;
return true;
}
else
{
Node* leftMaxParent = cur;
Node* leftMax = cur->_left;
if (leftMax->_right == nullptr)
{
leftMax->_right = cur->_right;
delete cur;
parent->_left = leftMax;
return true;
}
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, cur->_key);
leftMaxParent->_right = leftMax->_left;
delete leftMax;
leftMax = nullptr;
return true;
}
}
else
{
if (cur->_left == nullptr)
{
parent->_right = cur->_right;
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
parent->_right = cur->_left;
delete cur;
return true;
}
else
{
Node* leftMaxParent = cur;
Node* leftMax = cur->_left;
if (leftMax->_right == nullptr)
{
leftMax->_right = cur->_right;
delete cur;
parent->_right = leftMax;
return true;
}
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, cur->_key);
leftMaxParent->_right = leftMax->_left;
delete leftMax;
leftMax = nullptr;
return true;
}
}
}
}
return false;
}
bool Erase2(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)
{
if (parent == nullptr)
{
_root = cur->_right;
}
else if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else if (parent->_right == cur)
{
parent->_right = cur->_right;
}
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else if (parent->_right = cur)
{
parent->_right = cur->_left;
}
}
else
{
Node* leftMax = cur->_left;
Node* leftMaxParent = cur;
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
std::swap(cur->_key, leftMax->_key);
if (leftMaxParent->_left == leftMax)
{
leftMaxParent->_left = leftMax->_left;
}
else
{
leftMaxParent->_right = leftMax->_left;
}
cur = leftMax;
}
delete cur;
return true;
}
}
}
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:
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* Copyroot = new Node(root->_key);
Copyroot->_left = Copy(root->_left);
Copyroot->_right = Copy(root->_right);
return Copyroot;
}
void _Destory(Node*& root)
{
if (root == nullptr)
{
return;
}
_Destory(root->_left);
_Destory(root->_right);
delete root;
root == nullptr;
}
bool _EraseR(Node*& root, const K& key)
{
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
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
Node* leftMax = root->_left;
while (leftMax->_right)
{
leftMax = leftMax->_right;
}
std::swap(leftMax->_key, root->_key);
return _EraseR(root->_left, key);
}
delete del;
return true;
}
}
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (root->_key < key)
{
return _InsertR(root->_right, key);
}
else if (root->_key > key)
{
return _InsertR(root->_left, key);
}
else
{
return false;
}
}
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key == key)
{
return true;
}
else if (root->_key > key)
{
return _FindR(root->_left, key);
}
else
{
return _FindR(root->_right, key);
}
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
private:
Node* _root;
};
对于上面的代码,我们可以使用如下代码进行测试
#include<iostream>
#include<algorithm>
using namespace std;
#include"BinarySearchTree.h"
void Test1()
{
BSTree<int> root;
int arr[] = { 8,3,1,10,6,4,7,14,13,5,10,15,20,21,22,32,2 };
for (auto& e : arr)
{
root.Insert(e);
}
root.InOrder();
cout << endl;
for (auto& e : arr)
{
root.Erase1(e);
root.InOrder();
cout << endl;
}
BSTree<int> root2;
for (auto& e : arr)
{
root2.Insert(e);
}
root2.InOrder();
cout << endl;
for (auto& e : arr)
{
root2.Erase2(e);
root2.InOrder();
cout << endl;
}
cout << root2.FindR(1) << endl;
}
void Test2()
{
BSTree<int> root;
int arr[] = { 8,3,1,10,6,4,7,14,13,5,10,15,20,21,22,32,2 };
for (auto& e : arr)
{
root.InsertR(e);
}
root.InOrder();
cout << endl;
cout << root.FindR(1) << endl;
for (auto& e : arr)
{
root.EraseR(e);
root.InOrder();
cout << endl;
}
cout << root.FindR(1) << endl;
}
void Test3()
{
BSTree<int> root;
int arr[] = { 8,3,1,10,6,4,7,14,13,5 };
for (auto& e : arr)
{
root.InsertR(e);
}
root.InOrder();
cout << endl;
BSTree<int> r1(root);
r1.InOrder();
cout << endl;
BSTree<int> t2 = r1;
t2.InOrder();
}
int main()
{
Test3();
return 0;
}
最终结果都是符合我们的预期的。
总结
本文详细介绍了二叉搜索树的概念和实现,二叉搜索树的删除是最麻烦的,因为它的考虑的情况确实是非常多的,也确实是比较难的。希望读者可以好好品味