从零开学C++:二叉搜索树

引言:在本篇博客当中,我们会将关于二叉树的进阶结构——二叉搜索树,强大的搜索效率让它在数据结构当中变得十分重要,让我们一起来进行学习吧!

更多有关C++的知识详解可前往个人主页:计信猫

一,二叉搜索树的概念

        二叉搜索树的概念其实很简单,首先它一定满足成为一颗二叉树的基本条件,其次值比根节点小的储存在左子树,值比根节点大的储存在右子树,同时它的左右子树也为二叉搜索树。如图就是一颗简单的二叉搜索树

二,二叉搜索树的创建

        既然我们想创建一颗二叉搜索树,那么首先我们就应该创建一个二叉搜索树节点结构体(我们称为Node),如下代码所示:

template<class k>
class Node
{
public:
	Node(const k& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
	Node* _left;
	Node* _right;
	k _key;//key为所储存的值
};

        此后我们还应该创建一个结构体专门用于二叉搜索树的遍历,数据操作,代码如下:

template<class k>
class BSTree
{
public:
	using Node = Node<k>;
    Node* _root = nullptr;
};

三,二叉搜索树的数据操作函数

1,插入数据

        在二叉搜索树中,我们想插入一个值其实就非常简单了,我们分为两种情况:

        情况一:当二叉搜索树为一个空树时,那么我们就直接创建一个节点并且将这个节点赋值给_root即可。

        情况二:即不为空树时,那么我们只需要根据二叉搜索树的规则遍历到二叉树的根节点,再把数据插入即可。

        所以我们的代码如下:

//插入数据
bool insert(const k& key)
{
	//情况一:根节点为空
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	//情况二:根节点不为空
	else
	{
		//比根节点大的放左边,比根节点小的放右边
		Node* cur = _root;
		Node* parent = nullptr;
		//cur遍历到根节点
		while (cur)
		{
			if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(key);
		//比父节点小就插入左边,反之则插入右边
		if (parent->_key > key)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		return true;
	}

 2,查找数据

        该函数的作用是查找二叉搜索树中是否存在值key,如果在遍历的过程中找到了值key,那么就返回true,若未找到值key,就返回false。那么代码如下:

//查找数据
bool find(const k& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)//比父节点大就往右走
		{
			cur = cur->_right;
		}
		else if (cur->_key < key)//比父节点小就往左走
		{
			cur = cur->_left;
		}
		else//相等就返回true
		{
			return true;
		}
	}
	return false;
}

 3,删除数据

        删除数据函数在本篇博客中就相对复杂,但没事,我将分情况来进行一一地讲解。

(1)删除的节点左右子树都为空

        这种情况就非常简单,删除这种叶子节点(cur)我们只需要将其父节点的左右指针指向空,然后delete掉这个叶子节即可,如下图所示:

        而该种情况可以被我们直接归类为情况(2)或情况(3) 。

(2)删除的节点左子树不为空,右子树为空

        当我们遇到这种情况的时候,我们就可以将其父节点(parent)与这个节点(cur)的左孩子相连,但连接的时候我们同时需要判断这个节点与父节点的左右关系如果被删除节点在父节点的左边,那么我们就应该将该节点的左子树插入到父节点的左边,反之我们则插入父节点的右边。如下图所示:

        可一旦当我们遇到如下图的情况就要进行特殊处理:

        其实处理办法也很简单,我们只需要根节点的左孩子赋值给_root,然后再删除cur节点即可。所以该情况下处理的代码如下:

else if (cur->_right == nullptr)//cur的右边为空
{
	if (cur == _root)//特殊情况
	{
		_root = cur->_left;
	}
	else
	{
		if (parent->_left == cur)//左孩子被删除
		{
			parent->_left = cur->_left;
		}
		else//右孩子被删除
		{
			parent->_right = cur->_left;
		}
	}
	delete cur;
	return true;
}

(3)删除的节点左子树为空,右子树不为空

        那么这种情况其实就可以直接类比到情况(2)了,此时我们就可以直接展示代码:

if (cur->_left == nullptr)//cur的左边为空
{
	if (cur == _root)//特殊情况
	{
		_root = cur->_right;
	}
	else
	{
		if (parent->_left == cur)//左孩子被删除
		{
			parent->_left = cur->_right;
		}
		else//右孩子被删除
		{
			parent->_right = cur->_right;
		}
	}
	delete cur;
	return true;
}

(4)删除的节点左子树和右子树都不为空

        这种情况是最麻烦的一种,但是不用担心,静下心来慢慢理解,你会发现易如反掌。那让我们以下图为例子:

        那么解决这种问题,我们就会用到一种方法,叫做替换法替换法的含义是找到左子树的最右节点或者右子树的最左节点,来替换掉要删除的节点,再删除需要被删除的节点。语言听着很晦涩,那么我们以图来说明!

        这样,一个节点就成功被我们删除了!但是要注意,连接节点的时候还是要进行判断,若被删除的节点是在其父节点的左边,那么替换的节点也必须被连接在父节点的左边,同理,右边也是一样的。所以我们的代码如下:

//删除数据
bool erase(const k& key)
{
	Node* cur = _root;
	Node* parent = nullptr;
	//使用cur先遍历到要删除的节点的位置
	while (cur)
	{
		if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//相等,进行删除操作
			if (cur->_left == nullptr)//cur的左边为空
			{
				if (cur == _root)
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_left == cur)//左孩子被删除
					{
						parent->_left = cur->_right;
					}
					else//右孩子被删除
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
				return true;
			}
			else if (cur->_right == nullptr)//cur的右边为空
			{
				if (cur == _root)
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_left == cur)//左孩子被删除
					{
						parent->_left = cur->_left;
					}
					else//右孩子被删除
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
				return true;
			}
			else//左右均不为空,使用替换法删除:左子树的最大值或者右子树的最小值
			{
				Node* replace = cur->left;
				Node* replaceparent = cur;
				while (replace->_right)
				{
					replaceparent = replace;
					replace = replace->_right;
				}
				cur->_key = replace->_key;
				if (replaceparent->_left == replace)
				{
					replaceparent->_left = replace->_left;
				}
				else
				{
					replaceparent->_right = replace->_left;
				}
				delete replace;
				return true;
			}
		}
	}
	return false;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值