[C++]二叉搜索树

本文详细介绍了二叉搜索树的基本概念、插入、查找、删除操作以及中序遍历的实现,包括BSTree类的模板和关键方法的源码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、定义

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

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

二、插入insert

对于二叉搜索树的插入操作,我们将需要插入的key值与当前结点(初始结点是root结点)比较,若小于该结点的值,则往左子树走;若大于该结点的值,则往右子树走。走到空结点的时候,那么这个位置就是这个key值的归宿。

template<class K, class V>
struct BSTreeNode
{
	BSTreeNode<K, V>* left;
	BSTreeNode<K, V>* right;

	K _key;
	V _value;
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	BSTree()
	{
		_root = nullptr;
	}


	bool Insert(const K& key, const V& value)
	{
		if (_root == nullptr)
		{
			_root = new Node;
			_root->_key = key;
			_root->_value = value;
			_root->left = nullptr;
			_root->right = nullptr;
		}
		else
		{
			Node* cur = _root;
			Node* parent = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->left;

				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->right;
				}
			}
			cur = new Node;
			cur->left = nullptr;
			cur->right = nullptr;
			cur->_key = key;
			cur->_value = value;

			if (key < parent->_key)
			{
				parent->left = cur;
			}
			else
			{
				parent->right = cur;
			}

		}

		return true;
	}
private:


	Node* _root = nullptr;
};

三、查找操作find

对于二叉搜索树的查找操作,我们将需要查找的key值与当前结点(初始结点是root结点)比较,若小于该结点的值,则往左子树走;若大于该结点的值,则往右子树走。

若走到空,则说明没有这个值,返回空指针。若找到该值,则返回该值的结点。

	Node* Find(const K& key)
	{
		Node* cur = _root;

		while (cur)
		{
			if (key < cur->_key)
			{
				cur = cur->left;
			}
			else if (key > cur->_key)
			{
				cur = cur->right;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}

四、删除操作

	bool Erase(const K& key)
	{
		Node* cur = _root;
		Node* parent = _root;
		while (cur)
		{
			//若key小于当前结点的值,则往左子树走
			if (key < cur->_key)
			{
				parent = cur;
				cur = cur->left;
			}

			//若key大于当前结点的值,则往右子树走
			else if (key > cur->_key)
			{
				parent = cur;
				cur = cur->right;
			}
			//若key值与当前结点的值相等,则说明该结点就是需要删除的
			else
			{
				//该结点左右子树皆为空的情况
				if (cur->left == nullptr && cur->right == nullptr)
				{
					//直接删除
					if (key < parent->_key)
					{
						parent->left = nullptr;
					}
					if (key > parent->_key)
					{
						parent->right = nullptr;
					}

					delete cur;
				}
				//该结点左子树为空的情况
				else if (cur->left == nullptr)
				{
					//将该结点的左子树链接到该结点的父结点的子树上,然后删除该结点
					if (key < parent->_key)
						parent->left = cur->right;
					else
						parent->right = cur->right;
					delete cur;
				}
				//该结点右子树为空的情况
				else if (cur->right == nullptr)
				{
					//将该结点的右子树链接到该结点的父结点的子树上,然后删除该结点
					if (key < parent->_key)
						parent->left = cur->left;
					else
						parent->right = cur->left;
					delete cur;
				}
				//左右子树皆不为空
				//该情况下,我们需要找到左子树的最大结点/右子树的最小结点
				//然后将需要删除的结点的值和找到的最大结点/最小结点的值交换
				//此时我们只需要删除交换之后值为需要删除的数值的结点
				//这里以与左子树的最大结点交换为例
				else
				{
					//del是之后需要删除的结点
					Node* del = cur->left;
					//tmp是需要删除的结点的父结点
					Node* tmp = del;
					//找到左子树的最大结点,记录为del,此时tmp为del的父结点
					while (del->right)
					{
						tmp = del;
						del = del->right;

					}
					//交换key值
					swap(cur->_key, del->_key);

					//如即将被删除的结点有左子树,则将其链接到tmp上
					if (del->left)
					{
						tmp->right = del->left;
					}
					//否则直接将tmp指向即将被删除的结点的指针置空
					else
					{
						tmp->right = nullptr;
					}

					//删除交换key值后的结点
					delete del;
					
				}

				return true;
			}
			
		}

		return false;
	}

五、中序遍历InOrder

使用中序遍历二叉搜索树时,我们得到的是一个递增序列。

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->left);
		cout << root->_key << ' ';
		_InOrder(root->right);
	}


	void InOrder()
	{
		_InOrder(_root);

		cout << endl;
	}

六、完整代码

#include<iostream>
#include<string>



template<class K, class V>
struct BSTreeNode
{
	BSTreeNode<K, V>* left;
	BSTreeNode<K, V>* right;

	K _key;
	V _value;
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	BSTree()
	{
		_root = nullptr;
	}


	bool Insert(const K& key, const V& value)
	{
		if (_root == nullptr)
		{
			_root = new Node;
			_root->_key = key;
			_root->_value = value;
			_root->left = nullptr;
			_root->right = nullptr;
		}
		else
		{
			Node* cur = _root;
			Node* parent = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->left;

				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->right;
				}
			}
			cur = new Node;
			cur->left = nullptr;
			cur->right = nullptr;
			cur->_key = key;
			cur->_value = value;

			if (key < parent->_key)
			{
				parent->left = cur;
			}
			else
			{
				parent->right = cur;
			}

		}

		return true;
	}


	Node* Find(const K& key)
	{
		Node* cur = _root;

		while (cur)
		{
			if (key < cur->_key)
			{
				cur = cur->left;
			}
			else if (key > cur->_key)
			{
				cur = cur->right;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}



	bool Erase(const K& key)
	{
		Node* cur = _root;
		Node* parent = _root;
		while (cur)
		{
			//若key小于当前结点的值,则往左子树走
			if (key < cur->_key)
			{
				parent = cur;
				cur = cur->left;
			}

			//若key大于当前结点的值,则往右子树走
			else if (key > cur->_key)
			{
				parent = cur;
				cur = cur->right;
			}
			//若key值与当前结点的值相等,则说明该结点就是需要删除的
			else
			{
				//该结点左右子树皆为空的情况
				if (cur->left == nullptr && cur->right == nullptr)
				{
					//直接删除
					if (key < parent->_key)
					{
						parent->left = nullptr;
					}
					if (key > parent->_key)
					{
						parent->right = nullptr;
					}

					delete cur;
				}
				//该结点左子树为空的情况
				else if (cur->left == nullptr)
				{
					//将该结点的左子树链接到该结点的父结点的子树上,然后删除该结点
					if (key < parent->_key)
						parent->left = cur->right;
					else
						parent->right = cur->right;
					delete cur;
				}
				//该结点右子树为空的情况
				else if (cur->right == nullptr)
				{
					//将该结点的右子树链接到该结点的父结点的子树上,然后删除该结点
					if (key < parent->_key)
						parent->left = cur->left;
					else
						parent->right = cur->left;
					delete cur;
				}
				//左右子树皆不为空
				//该情况下,我们需要找到左子树的最大结点/右子树的最小结点
				//然后将需要删除的结点的值和找到的最大结点/最小结点的值交换
				//此时我们只需要删除交换之后值为需要删除的数值的结点
				//这里以与左子树的最大结点交换为例
				else
				{
					//del是之后需要删除的结点
					Node* del = cur->left;
					//tmp是需要删除的结点的父结点
					Node* tmp = del;
					//找到左子树的最大结点,记录为del,此时tmp为del的父结点
					while (del->right)
					{
						tmp = del;
						del = del->right;

					}
					//交换key值
					swap(cur->_key, del->_key);

					//如即将被删除的结点有左子树,则将其链接到tmp上
					if (del->left)
					{
						tmp->right = del->left;
					}
					//否则直接将tmp指向即将被删除的结点的指针置空
					else
					{
						tmp->right = nullptr;
					}

					//删除交换key值后的结点
					delete del;
					
				}

				return true;
			}
			
		}

		return false;
	}


	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->left);
		cout << root->_key << ' ';
		_InOrder(root->right);
	}


	void InOrder()
	{
		_InOrder(_root);

		cout << endl;
	}


private:


	Node* _root = nullptr;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杯酒问苍天

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值