【C++】 二叉搜索树复习+模拟实现

前言

最近把笔记软件改成了obsidian了,也是一个朋友给我推荐的。语雀其实挺好用的,但是感觉obsidian的上限更高,并且在看了卡片盒笔记法后,我觉得知识是要成网络的。而语雀没有这样的能力,obsidian的网络节点功能能更好地帮我梳理知识点之间的关系。好的工具是大脑的延申嘛 现在的问题是,我语雀的快捷键用的很习惯了,而obsidian的快捷键还在熟悉阶段,一些功能也没有,书写也全是markdown格式,需要慢慢熟悉了。但我相信等我熟悉了之后,效率会得到提高。以前语雀记录的内容,往往很少去复习,后面使用obsidian了,我希望我能有效地复习,把知识点都串联起来 人总是要不断地踏出舒适圈,才能得到最快地提升,包括后面我可能去练习一下vim的操作,不过那是后话了~
今天我们来看一下二叉搜索树,作为学习map/set的前置知识


二叉搜索树

什么是二叉搜索树?其实刷过Leetcode我们应该都不陌生,它本质上是一种二分的思想,通过二分法,将线性查找的时间复杂度优化到了对数时间复杂度。二叉搜索树也很简单,根据递归的思想,它只需要满足以下性质

  1. 左子树的所有节点都小于根节点的值
  2. 右子树的所有节点都大于根节点的值
  3. 左右子树同时满足上述条件

光靠看是没用的,这里放一些二叉搜索树的题目,做一做,相信大家对其的理解会加深不少


实现思路

我们要实现一个二叉搜索树,就要考虑它的增删改查,其中改比较简单,通过查找到它的位置后修改对应的值即可,所以这里我们讲一下增删查的思路。

从根节点开始搜索,比根节点小走左节点,比根节点大走右节点
到空节点都未找到,说明目标节点不存在

树为空,直接插入并返回
按照查的思路,找到插入位置,插入新节点

查找要删除的节点是否存在,不存在返回,存在则根据目标节点以下状态:

  1. 只有左孩子
  2. 只有右孩子
  3. 没有子节点
  4. 既有左孩子也右孩子
    其中12两种情况是:直接将被删除节点的父节点指向被删除节点的子节点,4则需要在左子树中找到最大节点(或者右子树中找到最小节点)与被删除节点互换,并删除该节点。

代码实现

namespace mystd 
{
	template<class T>
	struct BSTreeNode
	{
		BSTreeNode* _left;
		BSTreeNode* _right;
		T _value;
		BSTreeNode(T value)
			:_left(nullptr)
			,_right(nullptr)
			,_value(value)
		{}
	};

	template<class T>
	class BSTree
	{
		typedef BSTreeNode<T> Node;
	public:
		bool Insert(const T& x)
		{
			if (_root == nullptr)
			{
				_root._value = x;
				return true;
			}

			Node cur = _root;
			Node parent = nullptr;
			while (cur)
			{
				if (x < cur->_value)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (x > cur->_value)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
	
			cur = new Node(x);
			if (x < parent->_value)
				parent->_left = cur;
			else
				parent->_right = cur;
			
			return true;
		}
		
		bool Find(const T& x)
		{
			Node* cur = _root;
			while (cur)
			{
				if (x < cur->_value)
					cur = cur->_left;
				else if (x > cur->_value)
					cur = cur->_right;
				else
					return true;
			}
			return false;
		}

		bool Erase(const T& x)
		{
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (x < cur->_value)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (x < cur->_value)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					// 左子树空
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_right;
							else
								parent->_right = cur->_left;
						}
						delete cur;
					}
					// 右子树空
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left = cur)
								parent->_left = cur->_left;
							else
								parent->_right = cur->_left;
						}
						delete cur;
					}
					else
					{
						Node* leftMaxParent = cur;
						Node* leftMax = cur->_left;

						while (leftMax->_right)
						{
							leftMaxParent = leftMax;
							leftMax = leftMax->_right;
						}

						cur->_value = leftMax->_value;

						if (leftMaxParent->_left == leftMax)
							leftMaxParent->_left = leftMax->_left;
						else
							leftMaxParent->_right = leftMax->_left;
						
						delete leftMax;
					}
					return true;
				}

			}	
			return false;
		}
	private:
		Node _root = nullptr;
	};
}

结束语
自己的代码能力确实依然有所欠缺,二叉搜索树依然花了大量的时间才写好。可能明天看能不能看完set/map的源码吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值