【C++】二叉搜索树

1. 二叉搜索树的概念

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

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

例:int a [ ]={5,3,4,1,7,8,2,6,0,9}构成的二叉搜索树如下:
在这里插入图片描述

二叉搜索树的特点:中序遍历后会得到一个升序排列的数组。

2. 二叉搜索树的操作
2.1 查找 key

其中:右树的值 > 根节点的值 > 左树的值
若根节点不为空:
若根节点的值value==key 则返回当前节点
若根节点的值value > key 则在其左树继续查找
若根节点的值value < key 则在其右树继续查找
否则返回空值 nullptr

Node* Find(const K& value)//查找一个元素,时间复杂度为O(logN)
		{
			Node* cur = _root;
			while (cur)
			{
				if (value > cur->_val)
					cur = cur->_right;
				else if (value < cur->_val)
					cur = cur->_left;
				else
					return cur;   //找到返回当前结点
			}
			return nullptr;      //找不到返回空值
		}
2.2 插入

若树为空则直接插入
若树不为空,则按二叉搜索树的性质确定插入位置,再进行插入
插入成功返回true,插入失败(插入相同的值)返回false

bool Insert(const K& value)//插入一个元素
		{
			if (_root == nullptr)//若树为空时,可直接插入
			{
				_root = new Node(value);
				return true;
			}
			//树不空时,先确定位置再插入
			Node* cur = _root;
			Node* parent = nullptr;//记录cur的双亲节点,方便插入新节点
			while (cur)
			{
				parent = cur;
				if (val > cur->_data)   //val大于当前节点的值就向右找
					cur = cur->_right;
				else  if (val < cur->_data)    //val 小于当前节点的值就向左找
					cur = cur->_left;
				else                    //val 等于当前节点的值,不允许插入
					return false;
			}
			//找到对应位置,插入元素并与树连接
			cur = new Node(value);
			if (value > parent->_val)
				parent->_right = cur;
			else
				parent->_left = cur;

			return true;
 }
2.3 删除

这里无法使用std::find确定位置,因为需要同时知道当前节点的双亲节点
我们应该首先查找元素是否在二叉搜索树中,若不存在则直接返回,若存在则删除。删除时会有以下几种情况:
1)要删除的结点是叶子节点或只有左孩子
则将被删除结点的双亲节点指向被删除节点的左孩子
2)要删除的结点只有右孩子
则将被删除结点的双亲节点指向被删除节点的右孩子
3)要删除的结点既有左孩子也有右孩子
则在被删除结点的左子树中找最右结点(或者在右子树中最左结点)与要删除的结点交换值,再删除最值结点。

bool Erase(const K& value)//删除一个元素
		{
			if (_root == nullptr)//如果树为空,删除失败
				return false;
			
			//查找元素在二叉搜索树中应该删除的位置
			Node* cur = _root;
			Node* parent = nullptr;//记录cur的双亲节点,方便插入新节点
			while (cur)
			{
				if (value > cur->_val)//若是查找的元素大于cur的值,到cur的右树中找
				{
					parent = cur;
					cur = cur->_right;
				}
					
				else if (value < cur->_val)
				{
					parent = cur;
					cur = cur->_left;
				}	
				else//已经找到,跳出循环
					break;
			}
			if (nullptr == cur)  return false;     //找不到,无法删除

			Node* del = cur;
			if (cur->_right == nullptr)   //1. 是叶子或只有左孩子
			{
			if (parent == nullptr)     //要删节点是根节点,则直接返回
					_root = cur->_left;
				else{
					if (parent->_left == cur)  //如果cur是父亲节点的左孩子
						parent->_left = cur->_left;
					else
						parent->_right = cur->_left;
				}
			}else if (cur->_left == nullptr)      //2. 只有右孩子
			{
				if (parent == nullptr)  
					_root = _root->_right;  //要删节点是根节点
				else{
					if (parent->_right == cur)
						parent->_left = cur->_right;
					else
						parent->_right = cur->_right;
					}
			}else                      //既有左孩子又有右孩子
			{
				Node* replace = cur->_right;     //用右树最左进行替代
				Node* pre = cur;         //替代节点的双亲节点,关键!!
				while (replace->_left)      //找到右树最左结点
				{
					pre = replace;
					replace = replace->_left;
				}
				cur->_data = replace->_data;
				if (pre->_left==replace)       //替代节点的双亲节点与它的下个结点连接
				pre->_left = replace->_right;   
				else pre->_right = replace->_right;
				del = replace;
			}
			delete del;
			return true;
	}
			
3.完整代码

在下篇博客:二叉搜索树的模拟实现中我分享了完整代码,敬请查看!

4.二叉树的性能分析

二叉搜索树的插入和删除都必须先查找,所以查找的效率也就是二叉树操作的性能。
对于n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点再二叉搜索树的深度的函数,即节点越深,比较次数越多。

但对于同一关键码的集合,如果各关键码插入次序不同,可能得到不同结构的二叉树:最优的情况下,这棵树为完全二叉树;最差的情况下,这棵树为单支树。

例如,{3,4,5,6,7,8,9}这个集合
若它按{6,4,3,5,8,7,9}插入结点时,就能构成完全二叉树,使得查找效率最高
在这里插入图片描述
若它按{3,4,5,6,7,8,9}插入结点时,只能构成右单支,查找效率最低
在这里插入图片描述

那么,我们有没有什么办法,能保证无论按照什么次序插入关键码,二叉树的性能都是最佳的呢?
正好在下篇博客,我讲到的AVL(平衡)二叉树,就能很好的完成这件事情。

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值