详解C++二叉搜索树

二叉搜索树前言:

二叉搜索树是红黑树的铺垫,红黑树是二叉搜索树的优化,学懂二叉树再学红黑树就会相对轻松一点

目录

  • 二叉搜索树的特点
  • 二叉树的操作
  • 二叉搜索树的实现
  • 二叉搜索树的缺点

二叉搜索树的特点

  1. 左子树比根结点的键小
  2. 右子树比根结点的键大
  3. 一棵树里没有键相等的两个结点

键值对介绍
键值对是什么?

键值对是一个能存储两个数据的结构体,数据分为键和值,其中一个数据(键)代表着整体数据(进行运算时只考虑其中一个数据,这个数据就叫做键)

为什么有键值对的出现?

通常我们需要将两个数据存储到同一容器,这样就能方便通过键直接找到值。
例如:我们不用键值对存储,我们就要将两个数据存储到两个容器,遍历一遍找到了一个数据,而又要找这个数据的对应数据时,又必须遍历另一个容器,会大大降低效率

二叉树的操作

二叉树的查找:

a、

要查找的数据x从根开始比较。
若x比根的数据小,则往根的左子树去找。
若x比根的数据大,则往根的右子树去找。

b、

最多查找高度次,若找到空,则说明该树里没这个数据

请添加图片描述
请添加图片描述

二叉树的插入:
a、

要插入的数据x从根开始比较。
若x比根的数据小,则往根的左子树去找。
若x比根的数据大,则往根的右子树去找。
若x和根的数据相等,则插入失败。(不允许有两个相等的数据)

b、

直到找到为空,创建一个新结点,再将新结点连接起来

请添加图片描述

二叉树的删除:
情况一:

要删除的结点无左孩子或右孩子
请添加图片描述

情况二:

要删除的孩子既有左孩子又有右孩子

这种情况就需要用替换法,删除替换掉的结点
谁去替换呢?

要删除的数据x,找到与x数据大小最相近的数据替换即可
a、找到x结点左子树的最右结点
b、或者找到x结点右子树的最左结点

a、找到x结点左子树的最右结点
请添加图片描述

b、找到x结点右子树的最左结点
请添加图片描述

二叉搜索树的实现

在这里插入图片描述
二叉树的插入:
在这里插入图片描述
二叉树的删除:
在这里插入图片描述

参考代码:


template<class K, class V>
struct BSTreeNode
{
	BSTreeNode(K key = K(), V val = V())
		:_key(key)
		, _val(val)
	{
	}

	K _key;
	V _val;
	BSTreeNode* _left = nullptr;
	BSTreeNode* _right = nullptr;
};



template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	bool Insert(const K& key, const V& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(key, val);
			return true;
		}
		Node* cur = _root;
		Node* prev = nullptr;
		while (cur)
		{
			if (cur->_key < key)
			{
				prev = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				prev = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		Node* newNode = new Node(key, val);
		if (prev->_key > key)
		{
			prev->_left = newNode;
			return true;
		}
		else
		{
			prev->_right = newNode;
			return true;
		}
	}
	void Erase(const K& key)
	{
		if (_root == nullptr)
			return;
		Node* cur = _root;
		Node* prev = nullptr;
		while (cur)
		{
			if (cur->_key > key)
			{
				prev = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				prev = cur;
				cur = cur->_right;
			}
			else
			{
				break;
			}
		}
		if (cur == nullptr)
			return;
		if (cur->_right == nullptr)
		{
			if (_root == cur)
			{
				_root = cur->_left;
			}
			else if (prev->_left == cur)
			{
				prev->_left = cur->_left;
			}
			else
			{
				prev->_right = cur->_left;
			}
			delete cur;
		}
		else if (cur->_left == nullptr)
		{
			if (_root == cur)
			{
				_root = cur->_right;
			}
			else if (prev->_left == cur)
			{
				prev->_left = cur->_right;
			}
			else
			{
				prev->_right = cur->_right;
			}
			delete cur;
		}
		else
		{
			Node* curFind = cur->_right;
			Node* FindPrev = cur;
			while (curFind->_left)
			{
				FindPrev = curFind;
				if (curFind)
					curFind = curFind->_left;
			}
			cur->_key = curFind->_key;
			if (FindPrev->_right == curFind)
			{
				FindPrev->_right = cur->_right;
			}
			else
			{
				FindPrev->_left = curFind->_right;
			}
			delete curFind;

		}

	}

	Node* 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
			{
				return cur;
			}
		}
		return nullptr;
	}


	void _InOrder(Node* _root)
	{
		if (_root == nullptr)
		{
			return;
		}
		_InOrder(_root->_left);
		cout << _root->_key << ":" << _root->_val << " ";
		_InOrder(_root->_right);
	}
	void InOrder()
	{
		_InOrder(this->_root);
		cout << endl;
	}



private:
	Node* _root = nullptr;
};


void test()
{
	BSTree<int, int> t1;
	t1.Insert(3, 33);
	t1.Insert(2, 22);
	t1.Insert(9, 99);
	t1.Insert(4, 44);
	t1.Insert(8, 88);
	t1.Insert(4, 44);
	t1.Insert(5, 55);
	t1.Insert(11, 1111);
	t1.InOrder();
	t1.Erase(3);
	t1.InOrder();

}

void test2()
{
	string strArr[] = { "西瓜", "西瓜", "西瓜", "苹果", "樱桃", "香蕉", "哈密瓜", "苹果" };
	BSTree<string, int> coutTree;
	for (auto& str : strArr)
	{
		BSTreeNode<string, int>* cur = coutTree.Find(str);
		if (cur == nullptr)
		{
			coutTree.Insert(str, 1);
		}
		else
		{
			cur->_val++;
		}
	}
	coutTree.InOrder();
}


二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

在这里插入图片描述
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N
问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续章节学习的AVL树和红黑树就可以上场了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值