前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【二叉树进阶】搜索二叉树的性能分析及其应用

【二叉树进阶】搜索二叉树的性能分析及其应用

作者头像
YIN_尹
发布2024-01-23 14:15:40
1830
发布2024-01-23 14:15:40
举报
文章被收录于专栏:YIN_尹的博客

前言

上一篇文章我们学习了搜索二叉树的实现,这篇文章我们来对搜索二叉树进行一个性能分析,并来讲解一下它的应用。

1. 二叉搜索树的性能分析

插入和删除操作都必须先查找,所以查找效率就代表了二叉搜索树中各个操作的性能

那大家思考一下,搜索二叉树的查找的时间复杂度是多少? 那这个其实在不同情况下是不一样的: 如果二叉搜索树处于比较平衡的情况(接近完全二叉树),比如这样的

在这里插入图片描述
在这里插入图片描述

这种情况最坏的查找无非也就查找高度次(那如果结点数量为N,它的高度通常保持在logN的水平),所以这样它的时间复杂度就是O(logN)。 但是,避免不了出现这样的情况

在这里插入图片描述
在这里插入图片描述

二叉搜索树退化为单支树(或者接近单支),那这时查找的时间复杂度就应该是O(N)

所以,二叉搜索树的查找的时间复杂度

最优情况下,二叉搜索树趋于平衡,其平均比较次数为:

log_2 N

,时间复杂度为O(logN) 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:

\frac{N}{2}

,时间按复杂度为O(N)

那么问题来了:

如果退化成单支树,二叉搜索树的性能就失去了。 那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续章节学习的AVL树和红黑树就可以上 场了。 这个到时候讲到再说…

2. 二叉搜索树的应用

2.1 K模型

搜索二叉树的第一个应用是K模型,什么是K模型呢,介绍一下:

其实呢就是一个在不在的问题。 K模型:K模型即只有key作为关键码,结构中只存储Key,关键码即为需要搜索的值。

比如:

给一个单词word,判断该单词是否拼写正确,具体方式如下: 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误

那我们上一篇实现的搜索二叉树其实就是按照K模型搞的,所以这里就不举具体的例子了。

2.2 KV模型

KV模型即每一个关键码key,都有与之对应的值Value,即结构中存储<Key, Value>的键值对。

该种方式在现实生活中非常常见:

比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对; 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

那接下来我们就来改造一下我们上一篇文章实现的搜索二叉树,改造成KV模型来简单解决一下上面提到的两个问题。

英汉互译

怎么改造呢,也很简单:

第一步:

我们可以把上一篇文章写的那个放到一个Key的命名空间,然后拷贝一份放到KV的命名空间里进行修改。

在这里插入图片描述
在这里插入图片描述

然后,大部分地方都不用变,把这几个地方改一下就行了

在这里插入图片描述
在这里插入图片描述

其实就是增加一个模板参数,结点里面增加一个成员_val; 然后涉及到的模板的地方和相关的成员函数改一下就行了 首先插入的函数肯定得改一下

在这里插入图片描述
在这里插入图片描述

ps:代码比较长,就不全部放出来了,最后会给大家源码,大家不清楚的地方可以看

然后查找我们要简单改一下

因为现在是KV模型,完成英汉互译,不像之前那样查到了返回true,查不到返回false 应该是查到返回对应的结点,通过结点我们可以拿到对应的val,查不到返回个nullptr。

在这里插入图片描述
在这里插入图片描述

然后删除其实就不用改了,因为删除还是用key去找对应的结点删除就行了。 至于剩下的,大家有兴趣可以自己修改一下,我们这里就直接把剩下的其它一些接口删掉了。

然后我们来写个程序测试一下:

在这里插入图片描述
在这里插入图片描述

运行看看

在这里插入图片描述
在这里插入图片描述

然后关于这里这个循环如何结束,我们上面提到可以输入Ctrl+Z,然后回车,这就是正常让它结束。 当然还可以输入Ctrl+c,直接就结束了,不过Ctrl+c是直接让进程终止。

在这里插入图片描述
在这里插入图片描述
统计次数

接下来我们再来搞一个,还是用KV模型:

在这里插入图片描述
在这里插入图片描述

现在有一些水果,我们想统计每种水果出现的次数。

可以怎么搞呢?

🆗,我们还是用KV来存储键值对,key是水果的名字,val是出现的次数。

在这里插入图片描述
在这里插入图片描述

然后: 遍历存储水果的string数组,判断每一个水果在不在countTree里面,不在,就添加进去,把val置成1,在的话,就只让val++就行了。 这样遍历一遍,就统计出次数了。

在这里插入图片描述
在这里插入图片描述

运行一下

在这里插入图片描述
在这里插入图片描述

就统计出来了。

3. 源码展示

3.1 KV结构改造

代码语言:javascript
复制
namespace kv
{
	template <class K, class V>
	struct BSTreeNode
	{
		BSTreeNode(const K& key, const V& val)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _val(val)
		{}

		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;
		K _key;
		V _val;
	};

	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* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			//走到这里cur为空,就是key应该插入的位置
			cur = new Node(key, val);
			//链接
			if (key < parent->_key)
				parent->_left = cur;
			if (key > parent->_key)
				parent->_right = cur;
			return true;
		}
		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					//key == cur->_key就是找到了,进行删除
					//1.左为空
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (cur == parent->_left)
								parent->_left = cur->_right;
							else
								parent->_right = cur->_right;
						}
						delete cur;
						cur = nullptr;
					}
					//2.右为空
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
								parent->_left = cur->_left;
							else
								parent->_right = cur->_left;
						}
						delete cur;
						cur = nullptr;
					}
					else//左右都不为空
					{
						//这里选择右树的最小结点(最左边)替换
						Node* pminRight = cur;
						Node* minRight = cur->_left;
						while (minRight->_left)
						{
							pminRight = minRight;
							minRight = minRight->_left;
						}
						cur->_key = minRight->_key;
						//删除替换结点
						if (pminRight->_left = minRight)
						{
							pminRight->_left = minRight->_right;
						}
						else
						{
							pminRight->_right = minRight->_right;
						}
						delete minRight;
						minRight = nullptr;
					}
					return true;
				}
			}
			//找不到直接返回false
			return false;
		}
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_key << root->_val << " ";
			_InOrder(root->_right);
		}
	private:
		Node* _root = nullptr;
	};
}

3.2 测试

代码语言:javascript
复制
void TestBSTree5()
{
	kv::BSTree<string, string> dict;
	dict.Insert("sort", "排序");
	dict.Insert("left", "左边");
	dict.Insert("right", "右边");
	dict.Insert("string", "字符串");
	dict.Insert("insert", "插入");
	dict.Insert("erase", "删除");

	string str;
	while (cin >> str)
	{
		auto ret = dict.Find(str);
		if (ret)
		{
			cout << ":" << ret->_val << endl;
		}
		else
		{
			cout << "无此单词" << endl;
		}
	}
}
代码语言:javascript
复制
void TestBSTree6()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };

	kv::BSTree<string, int> countTree;
	for (auto fruit : arr)
	{
		//kv::BSTreeNode<string, int>* ret = conutTree.Find(e);
		auto ret = countTree.Find(fruit);
		if (ret == nullptr)
		{
			countTree.Insert(fruit, 1);
		}
		else
		{
			++ret->_val;
		}
	}
	countTree.InOrder();
}
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1. 二叉搜索树的性能分析
  • 2. 二叉搜索树的应用
    • 2.1 K模型
      • 2.2 KV模型
        • 英汉互译
        • 统计次数
    • 3. 源码展示
      • 3.1 KV结构改造
        • 3.2 测试
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档