搜索二叉树

概念

什么是搜索二叉树?搜索二叉树有什么特点?
它的特点就是任意节点的左右子树,一个子树全都比该节点的值大,另一个子树全都比该节点的值要小

节点的结构

//节点
template<class K>
struct BSTNode
{
	BSTNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{

	}

	BSTNode<K>* _left;
	BSTNode<K>* _right;
	K _key;
};

节点的插入

搜索二叉树中另一个特点就是没有重复的值,如果插入的节点在该树中已经存在,那么就不会再进行插入,如果不存在那么就在树的叶子结点的null节点处,找到合适的位置进行插入
//循环实现

bool insert(const K& key)
	{
		Node* newnode = new Node(key);
		if (_root == nullptr)
		{
			_root = newnode;
			return true;
		}

		Node* cur = _root;
		Node* parent = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}

		if (parent->_key > key)
		{
			parent->_left = newnode;
		}
		else
		{
			parent->_right = newnode;
		}
		return true;
	}

//递归实现

bool _Insert(const K& key, Node*& root)//指针的引用
	{
		if (root == nullptr)//root此时是别名
		{
			root = new Node(key);
			return true;
		}

		if (root->_key > key)
		{
			return _Insert(key, root->_left);
		}
		else if (root->_key < key)
		{
			return _Insert(key, root->_right);
		}
		else
		{
			return false;
		}
		
	}

删除操作

如果找到那么久删除返回true,如果没找到则返回false
难点在于删除某一个节点后,其它的节点应该怎么改变,才能继续保证二叉搜索树的性质
对于要删除的节点有以下四种情况:
无孩子节点,只有左孩子,只有右孩子,左右孩子都有
解决方案:
无孩子节点:直接删除即可
只有左孩子节点:让左孩子节点代替该节点的位置即可
只有右孩子节点:让右孩子节点代替该节点位置即可
既有左孩子又有右孩子:两种解决方案(假定左子树比该节点小,右子树比该节点大),1.让左子树的最大节点代替该位置2.让右子树最小节点代替该位置
//循环实现

bool erase(const K& key)
{
Node* cur = _root;
Node* parent = _root;

while (cur)
{
if (cur->_key < key)
{
	parent = cur;
	cur = cur->_right;
}
else if (cur->_key > key)
{
	parent = cur;
	cur = cur->_left;
}
else//找到该节点了
{
	
	//找到节点时分为三种情况
	//1.该节点左为空 2.该节点右为空 3.该节点左右都不为空 //4.该节点左右都为空(被归类到第一种情况中了)
	if (cur->_left == nullptr)
	{
		if (cur == _root)//根节点就是要找的节点
		{
			_root = _root->_right;
		}
		else
		{

			if (parent->_left == cur)
			{
				parent->_left = cur->_right;
			}
			else
			{
				parent->_right = cur->_right;
			}
		}

	}
	else if (cur->_right == nullptr)
	{
		if (cur == _root)//根节点就是要找的节点
		{
			_root = _root->_left;
		}
		else
		{
			if (parent->_left == cur)
			{
				parent->_left = cur->_left;
			}
			else
			{
				parent->_right = cur->_left;
			}
		}
	}
	else//该节点左右都不为空
	{
		//找替代节点//从当前cur节点找该节点左子树的最大节点或者右子树的最小节点
		Node* parent = cur;
		Node* leftMax = cur->_left;
		
		while (leftMax->_right)
		{
			parent = leftMax;
			leftMax = leftMax->_right;
		}

		swap(leftMax->_key, cur->_key);//与替换节点交换值

		//考虑特殊情况 找到的替换节点不是最后一个节点,它还有左孩子的情况
		if (parent->_left == leftMax)
		{
			parent->_left = leftMax->_left;
		}
		else
		{
			parent->_right = leftMax->_left;
		}
		cur = leftMax;
}
	delete cur;
	return true;
	}
}
	//未找到
	return false;
}

//递归实现

bool Erase(const K& key)
	{
		return _Erase(key, _root);
	}

private:
	bool _Erase(const K& key, Node*& root)
	{
		if (root == nullptr)
		{
			return false;
		}

		if (root->_key > key)
		{
			return _Erase(key, root->_left);
		}
		else if (root->_key < key)
		{
			return _Erase(key, root->_right);
		}
		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;
				}
				
				swap(leftMax->_key, root->_key);
				//_Erase(key, root->_left);//最大节点可能还有左子孩子,所以还要继续递归删除---return
				return _Erase(key, root->_left);
			}
			delete del;
			return true;
		}
		
	}

搜索二叉树的遍历

搜索二叉树的中序遍历结果是有序的
//递归遍历

void _InOrder(Node* root)
{
	if (root == NULL)//NULL和nullptr区别是什么
	{
		return;
	}

	_InOrder(root->_left);
	cout << root->_key << " ";
	_InOrder(root->_right);
}

性能分析

对于二叉搜索树,如果是完全二叉树或者接近完全数,对于查找一个值的效率可以达到log2N,但是如果这棵树退化成单枝树或者或者接近单枝数,那么它的查找效率就变成了
O(N).

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦想很美

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

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

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

打赏作者

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

抵扣说明:

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

余额充值