【C++从0到王者】第二十七站:搜索二叉树

文章目录

  • 前言
  • 一、二叉搜索树的概念
  • 二、二叉搜索树的实现
    • 1.二叉树的结点定义
    • 2.二叉搜索树的结构
    • 3.二叉搜索树的构造函数
    • 4.二叉搜索树的插入(非递归)
    • 5.二叉搜索树的中序遍历(排序)
    • 6.二叉搜索树的查找(非递归)
    • 7.二叉搜索树的删除(非递归)
    • 8.二叉搜索树的查找(递归)
    • 9.二叉搜索树的插入(递归)
    • 10.二叉搜索树的删除(递归)
    • 11.二叉搜索树的销毁
    • 12.拷贝构造函数
    • 13.赋值运算符重载
  • 三、二叉搜索树的实现完整代码
  • 总结

前言

在前面我们其实已经分析过二叉树了,所谓二叉树就是度为2的树。而二叉树中,又有一种特殊的树称之为二叉搜索树

一、二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

【C++从0到王者】第二十七站:搜索二叉树_第1张图片

【C++从0到王者】第二十七站:搜索二叉树_第2张图片

为什么我们将这样的树称作二叉搜索树呢?因为这样的树天生就十分适合搜索。是想一下,使用上图中最右边的树,如果我们想找到7的话,首先7比6大,所以一定在右子树,而7小于9,所以一定在左子树。结果发现就找到了。从而我们可以发现,这种树的效率天生就很高。

而且这棵树最多找高度次。也就是说最多找N次(注意不是logN次,因为我们可能碰到下图中右边的树),下图中的两棵树都是二叉搜素树。也就是说时间复杂度为O(N)

【C++从0到王者】第二十七站:搜索二叉树_第3张图片

普通的二叉搜索树的时间复杂度为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.二叉搜索树的插入(非递归)

对于插入接口,我们需要考虑的情况有如下几种:

  1. 二叉树为空的时候,直接new一个结点插入上去即可
  2. 如果二叉树不为空,那么我们就需要进行比较
    • 如果我们想要插入的值大于当前结点的值,那么我们就将当前结点迭代到右孩子
    • 如果我们想要插入的值小于当前结点的值,那么我们将当前结点迭代到左孩子
    • 如果恰好等于当前结点的值,那么我们可以认为插入失败了,因为此时已经不满足二叉搜索树的定义了。
    • 最终我们会发现如此迭代下去,最终的当前结点一定会为空指针,这并非我们所期望的。我们肯定是还需要它的父节点的,这样才能让我目标值插入进去。所以我们还需要一个值来记录当前所迭代位置的父节点。即我们下面代码的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.二叉搜索树的删除(非递归)

对于二叉搜索树的删除操作,其实是比较难的。这里出现了至少十种以上的情形。

【C++从0到王者】第二十七站:搜索二叉树_第4张图片

  1. 找目标结点。这里的找目标结点,我们采用与插入法类似的操作,有一个当前结点和一个父结点。一开始自然是cur为根节点,parent为nullptr。然后我们进行迭代, 当cur不为空的时候,如果当前cur里面的值大于目标值,则parent迭代为cur,cur迭代为左孩子。如果cur里面的值小于目标值的话,则parent迭代为cur,cur迭代为右孩子。如果相等的情况下,由于比较复杂,我们在后面在进行讨论。如果迭代下去。只要没有出现相等的情形,迟早会离开循环,离开循环了,那么直接返回false即可

  2. 如果中间遇到相等的情况,也就是说,我们已经找到了目前要删除的值,这个值就是cur所指向的结点。这个时候,我们需要进一步将其进行分析。

    • 这里存在的主要问题就是cur与parent以及cur的孩子之间的关系比较复杂。

    • 首先cur的孩子就分为四种情况,分别是没有孩子、只有左孩子、只有右孩子、两个孩子都有。由于我们要删除cur结点,所以孩子的情况的不同,我们所需要的处理方式就不一样。

      • 如果没有孩子的话,那么其实我们只需要直接删除该结点,然后将parent所指向cur的位置给置空即可。如下图所示:即当我们想要在下面这棵树中删除13的时候的情形

        【C++从0到王者】第二十七站:搜索二叉树_第5张图片

      • 如果cur只有一个左孩子,也就是说右孩子为空。那么我们可以,使用托孤的方式,即将cur的左孩子直接与parent连接起来,然后在直接删除cur即可

        【C++从0到王者】第二十七站:搜索二叉树_第6张图片

      • 如果cur只有一个右孩子,也就是说,左孩子为空,那么我们可以和上面一样采用类似的手段,托孤,将 cur的右孩子直接与parent进行连接,然后删除cur

      【C++从0到王者】第二十七站:搜索二叉树_第7张图片

      • 如果两个孩子都存在的话,那么此时情形比较复杂,我们采用替代法。替代法的基本思路是,将要删除的该结点视为一棵子树,然后找到这棵树的左子树的最大值,或者右子树的最小值。然后我们与这两个中的任何一个进行替换即可,然后删除原来被替换的结点。如下图所示、

        【C++从0到王者】第二十七站:搜索二叉树_第8张图片

      其实关于以上的cur与其孩子的四种情形,可以简化为三种情况:即将没有孩子归为只有一个左孩子或者只有一个右孩子的情形,因为我们可以将他们的孩子视为一个nullptr,如果这样思考的话,那么前三种方案都是使用托孤的思路。唯独第四种,我们需要使用替代法进行解决

    • 除了cur孩子之间的问题以外,还有parent和cur之间的关系也是存在一些情况的。

      • cur为parent的左孩子或右孩子,这种情形是比较简单的,也是前面图中的情形。我们只需要与cur与它的孩子的情况一关联即可。其实就是前面cur与其孩子的变化。

      • parent不存在,即cur就是根节点,这种情况我们需要进行特殊处理,因为此时已经不存在父节点了

        ① 当cur的左孩子或者右孩子任意一个为空的时候,此时我们采用托孤的处理方案,不过此时的托孤需要一些变化,因为cur其实就是_root,所以我们直接让_root赋值为cur->_left或者cur->_right即可。

        【C++从0到王者】第二十七站:搜索二叉树_第9张图片

        ②当cur的左孩子和右孩子都存在的时候,我们还是使用替代法

        如下是我们正常时候的替代,变化还不是很大。

        【C++从0到王者】第二十七站:搜索二叉树_第10张图片

        但是当恰好leftMax就是cur的左侧结点的时候,这时候就出现意外了,因为我们是需要进行连接的,所以需要知道leftMax的父节点是谁才可以方便连接,上图是因为leftMax肯定是它的右孩子,而此时变成了左孩子,所以需要特殊处理。(这里的特殊处理在前面的每一次使用替代法中都会出现这种情况)

        【C++从0到王者】第二十七站:搜索二叉树_第11张图片

根据以上的分析,我们已经可以写出以下的删除代码了:

由于这里存在三代人之间的关系,即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++从0到王者】第二十七站:搜索二叉树_第12张图片

那么我们不妨思考一下:前面的非递归形式,能否使用这种方式来进行迭代呢?其实是不可以的,这里能迭代是因为引用,永远都是那个空间,且由于在不同的栈帧原因导致了引用的指向被巧妙的改变了。而前者他们并没有使用引用,都是使用局部变量来进行迭代的,因为C++中的引用是不可以改变指向的。所以自然不可以。

如下图所示,非递归中,迭代的本质,仅仅只是局部变量的不断更替,不会直接影响root,这里局部变量的修改仅仅只是间接修改堆区已有数据的连接关系。如果直接赋值的话,只是修改了栈区的局部变量的数据,并未修改实际关系。

【C++从0到王者】第二十七站:搜索二叉树_第13张图片

如果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
#include
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;
}

最终结果都是符合我们的预期的。


总结

本文详细介绍了二叉搜索树的概念和实现,二叉搜索树的删除是最麻烦的,因为它的考虑的情况确实是非常多的,也确实是比较难的。希望读者可以好好品味

你可能感兴趣的:(【C++】,c++,数据结构,算法,c语言,开发语言)