二叉搜索树实现及性能分析

概念:

二叉搜索树(Binary Search Tree),(又称:二叉查找树,二叉排序树):它或者是一颗空树,或者是一颗具有以下性质的二叉树:

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

算法实现:

二叉搜索树的操作主要有:

1. 查找: 递归查找是否存在data.
在这里插入图片描述

2. 插入:

  • 树为空, 直接插入, 返回true
  • 树不为空, 按照二叉搜索树的性质查找插入位置, 插入新节点

3. 删除:

  • 要删除的结点为叶子结点: 直接删除.
  • 要删除的结点只有左子树结点,: 删除该结点后, 将它的左子树整体移动到删除结点的位置.
  • 要删除的结点只有右子树结点: 删除该结点后, 将它的右子树整体移动到删除结点的位置.
  • 要删除的结点左,右子树结点都存在: 直接删除的操作比较复杂, 可以在其子树中找到一个代替节点,用代替节点的值直接覆盖要删除的结点,从而转换成删除代替节点.-----代替结点的选择:可以选其左子树中的最大节点,即左子树中最右侧的结点, 或者其右子树中的最小结点, 即右子树中最左侧的结点.
template<class T>
struct BSTNode
{
	BSTNode(const T& data = T())
	: _pleft(nullptr), _pright(nullptr), _data(data)
	{
	}
	BSTNode<T>* _pleft;
	BSTNode<T>* _pright;
	T _data;
};

template<class T>
class BSTree
{
private:
	typedef BSTNode<T> Node;
	typedef Node* PNode;
public:
	PNode _proot;

public:
	// 构造
	BSTree()
		: _proot(nullptr)
	{}
	// 析构
	~BSTree()
	{
		Destory(_proot);
	};
	// 二叉搜索树的查找	
	PNode Find(const T& data)
	{
		
		// 思路: 首先结点不应为空 
		// 1. 如果节点的值 data = 查找的值data, 返回该节点
		// 2. 如果节点的值 data < 查找的值data, 在该结点的右子树中查找
		// 3. 如果节点的值 data > 查找的值data, 在该结点的左子树中查找
		// 4. 遍历完成未找到则返回 nullptr;
		PNode cur = _proot;
		while (cur)
		{
			if (cur->_data == data)
				return cur;
			else if (cur->_data < data)
			{
				cur = cur->_pright;
			}  else
				cur = cur->_pleft;
		}
		return nullptr;
	}
	
	bool Insert(const T& data)
	{
		// 思路:
		// 1. 如果根节点为空,则直接插入(根结点直接赋值) ---返回true
		// 2. 如果不为空, 则按照二叉搜索树性质 查找应该插入的位置,然后插入
		if (_proot == nullptr)
		{
			_proot = new Node(data);
			return true;
		}
		PNode cur = _proot;
		PNode parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (data < cur->_data)
				cur = cur->_pleft;
			else if (data > cur->_data)
				cur = cur->_pright;
			else
				return false;	// 如果插入的结点已经存在, 则插入失败
		}
		cur = new Node(data);
		// 将新插入的结点连接到树上
		if (data > parent->_data)
			parent->_pright = cur;
		else
			parent->_pleft = cur;
		return true;
	}
	bool Erase(const T& data)
	{
		// 如果树为空, 则直接返回false,删除失败
		if (_proot == nullptr)
			return false;
		
		// 遍历查找要删除结点, 并记录该节点的父节点
		PNode parent = nullptr;
		PNode cur = _proot;
		while (cur)
		{
			if (data == cur->_data)
				break;
			else if (data > cur->_data)
			{
				parent = cur;
				cur = cur->_pright;
			}
			else
			{
				parent = cur;
				cur = cur->_pleft;
			}
		}
		// 如果要删除结点不存在, 则删除失败
		if (cur == nullptr)
			return false;

		// 找到要删除结点后, 共有四种情况
		// 1.该结点无孩子结点
		// 2.该结点只有左孩子结点
		// 3.该结点只有右孩子结点
		// 4.该结点左右孩子结点都存在
		// 第一种情况可以与 2或3情况一并处理

		// 情况1,2
		if (cur->_pright == nullptr)
		{
			// 如果要删除的结点为根节点,且其只有左孩子结点
			if (cur == _proot)
			{
				_proot = cur->_pleft;
				//delete Node(cur);
			}
			else
			{
				if (cur == parent->_pleft)
					parent->_pleft = cur->_pleft;
				else
					parent->_pright = cur->_pleft;
			}
		}
		// 情况3
		else if (cur->_pleft == nullptr)
		{
			if (cur == _proot)
				_proot = cur->_pright;
			else
			{
				if (cur == parent->_pleft)
					parent->_pleft = cur->_pright;
				else
					parent->_pright = cur->_pright;
			}
		}
		// 情况4
		// 该结点左右子树都存在的情况,不能直接删除
		// 方法1: 在该结点的左子树中找到最大值(即最右侧的结点)将该结点的值赋给要删除的结点,然后在删除代替节点
		// 方法2: 在该结点的右子树中找到最小值---处理方式与方法1类似
		else
		{
			if (cur->_pleft != nullptr && cur->_pright != nullptr)
			{
				// 找左子树最大节点替换要删除的结点
				PNode replace = cur->_pleft;
				PNode parent = cur;
				while (replace->_pright)
				{
					parent = replace;
					replace = replace->_pright;
				}
				cur->_data = replace->_data;
				if (replace == parent->_pleft)
					parent->_pleft = replace->_pleft;
				else
					parent->_pright = replace->_pleft;
				delete replace;
				replace = nullptr;
			}
			return true;
		}
		return false;
	}
	private:
		void Destory(PNode _proot)
		{
			if (_proot)
			{
				Destory(_proot->_pleft);
				Destory(_proot->_pright);
			}
			_proot = nullptr;
		}
};

性能分析:

二叉搜索树的基本操作都是基于查找的, 查找的效率代表了二叉搜索树中其它操作的性能.
若一个二叉搜索树具有N个结点, 若每个元素查找效率相同, 则二叉搜索树的平均查找长度与结点在树中的深度成正比.
也就是说,不论哪一种操作, 所花费的时间都和树的深度成正比

  • 最优情况下, 二叉搜索树为 完全二叉树, 其平均比较次数为: log N
    在这里插入图片描述
  • 最差情况下, 二叉搜索树为 单支树, 其平均比较次数为: N/2
    在这里插入图片描述
    二叉搜索树虽然可以缩短查找的效率, 但是如果数据有序或者接近有序,二叉搜索树将会退化为单支树, 查找元素相当于在顺序表中搜索元素,效率低下. 由此引入了 AVL树.
    AVL树的实现及其性能分析
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值