二叉搜索树

个人主页: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;
			}
		}
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lei宝啊

觉得博主写的有用就鼓励一下吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值