C++ 二叉树

news2024/11/20 0:30:17

1. 二叉搜索树

1.1 二叉搜索树概念

二叉搜索树又称二叉排序树,他或者是一棵空树,或者是具有以下性质的二叉树:

①若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

②若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

③它的左右子树也分别为二叉搜索树

1.2 二叉搜索树操作

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

1.二叉搜索树的查找

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

b、最多查找高度次,走到空,还没找到,这个值不存在。

2.二叉搜索树的插入

插入的具体过程如下:

a、树为空,则直接新增节点,赋值给root指针

b、树不为空,按二叉搜索树性质查找插入位置,插入新节点。

3.二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回,否则要删除的节点可能分下面四种情况:

a、要删除的节点无孩子节点

b、要删除的节点只有左孩子节点

c、要删除的节点只有右孩子节点

d、要删除的节点有左、右孩子节点

看起来有待删除节点有四种情况,实际情况a可以与b、c结合起来,因此真正的删除过程如下:

*有一个孩子或者无孩子的节点被删除,则让被删除节点的双亲指向被删除节点的孩子或者空——直接删除

*有两个孩子的节点被删除,则在它的右子树中寻找最小的节点,用它的值填补到删除节点中,再来处理该节点的删除问题(符合二叉搜索树,左节点<根<右节点)——替换法删除

1.3 二叉搜索树的实现

树的节点

完整代码:

template <class T>
struct BSTNode
{
	T _key;
	BSTNode<T>* _left;
	BSTNode<T>* _right;

	BSTNode(const T& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{

	}
};

template <class T>
class BSTree
{
	typedef BSTNode<T> Node;

public:

	BSTree() = default;

	BSTree(const BSTree<T>& t)
	{
		_root = Copy(t._root);
	}

	~BSTree()
	{
		Destory(_root);
		_root = nullptr;
	}

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

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

		cur = new Node(key);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		return true;
	}

	Node* Find(const T& 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()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool Erase(const T& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				// 删除
				// 0-1个孩子的情况
				if (cur->_left == nullptr)
				{
					if (parent == nullptr)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
							parent->_left = cur->_right;
						else
							parent->_right = cur->_right;
					}

					delete cur;
					return true;
				}
				else if (cur->_right == nullptr)
				{
					if (parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
							parent->_left = cur->_left;
						else
							parent->_right = cur->_left;
					}

					delete cur;
					return true;
				}
				else
				{
					// 2个孩子的情况
					// 右子树的最小节点作为替代节点
					Node* rightMinP = cur;
					Node* rightMin = cur->_right;
					while (rightMin->_left)
					{
						rightMinP = rightMin;
						rightMin = rightMin->_left;
					}

					cur->_key = rightMin->_key;

					if (rightMinP->_left == rightMin)
						rightMinP->_left = rightMin->_right;
					else
						rightMinP->_right = rightMin->_right;

					delete rightMin;
					return true;
				}
			}
		}

		return false;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_key << " " << endl;
		_InOrder(root->_right);
	}

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* newroot = new Node(root->_key);
		newroot->_left = Copy(root->_left);
		newroot->_right = Copy(root->_right);

		return newroot;
	}

	void Destory(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		Destory(root->_left);
		Destory(root->_right);

		delete root;
	}

	Node* _root = nullptr;
};

 1.4 二叉搜索树的应用

1.K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

*以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树

*在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2. KV模型:每一个关键码key,都有与之对应的值Value,即的键值对。该种方式在现实生活中非常常见:

*比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文就构成一种键值对;

*再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是就构成一种键值对。

改造成kv结构的二叉搜索树的代码(与前面的逻辑基本上是一样的,无非就是多了一个值value)

完整代码:

using namespace std;

template <class T,class V>
struct BSTNode
{
	T _key;
	V _value;
	BSTNode<T,V>* _left;
	BSTNode<T,V>* _right;

	BSTNode(const T& key,const V& value)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
		,_value(value)
	{

	}
};

template <class T,class V>
class BSTree
{
	typedef BSTNode<T,V> Node;

public:

	BSTree() = default;

	BSTree(const BSTree<T, V>& t)
	{
		_root = Copy(t._root);
	}

	~BSTree()
	{
		Destory(_root);
		_root = nullptr;
	}

	bool insert(const T& key,const V& value)
	{
		if (_root == nullptr)
		{
			_root = new Node(key,value);
			return true;
		}

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

		cur = new Node(key,value);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		return true;
	}

	Node* Find(const T& 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()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool Erase(const T& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				// 删除
				// 0-1个孩子的情况
				if (cur->_left == nullptr)
				{
					if (parent == nullptr)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
							parent->_left = cur->_right;
						else
							parent->_right = cur->_right;
					}

					delete cur;
					return true;
				}
				else if (cur->_right == nullptr)
				{
					if (parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
							parent->_left = cur->_left;
						else
							parent->_right = cur->_left;
					}

					delete cur;
					return true;
				}
				else
				{
					// 2个孩子的情况
					// 右子树的最小节点作为替代节点
					Node* rightMinP = cur;
					Node* rightMin = cur->_right;
					while (rightMin->_left)
					{
						rightMinP = rightMin;
						rightMin = rightMin->_left;
					}

					cur->_key = rightMin->_key;

					if (rightMinP->_left == rightMin)
						rightMinP->_left = rightMin->_right;
					else
						rightMinP->_right = rightMin->_right;

					delete rightMin;
					return true;
				}
			}
		}

		return false;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_key << " " << root->_value << endl;
		_InOrder(root->_right);
	}

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* newroot = new Node(root->_key, root->_value);
		newroot->_left = Copy(root->_left);
		newroot->_right = Copy(root->_right);

		return newroot;
	}

	void Destory(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		Destory(root->_left);
		Destory(root->_right);

		delete root;
	}

	Node* _root=nullptr;
};

1.5 二叉搜索树的性能分析

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

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

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

 

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:$log_2 N$

最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:$\frac{N}{2}$

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么AVL树和红黑树就可以上场了,但是这边就不继续讲述AVL和红黑树了。

好了,今天就到这了,我们下次见~

有问题欢迎指正批评!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2166186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VIVADO IP核之FIR抽取器多相滤波仿真

VIVADO IP核之FIR抽取器多相滤波仿真&#xff08;含有与MATLAB仿真数据的对比&#xff09; 目录 前言 一、滤波器系数生成 二、用MATLAB生成仿真数据 三、VIVADO FIR抽取多相滤波器使用 四、VIVADO FIR抽取多相滤波器仿真 五、VIVADO工程下载 总结 前言 关于FIR低通滤波…

MySQL数据库修改authentication_string字段为显示密码后无法登录

MySQL数据库修改authentication_string字段为显示密码后无法登录 1. 本文创作背景2. 问题3. 原因分析4. 解决方案5. 附录 1. 本文创作背景 本文介绍MySQL数据库修改authentication_string字段为显示密码后无法登录的处理办法。 2. 问题 用户通过navicat修改MySQL的user表roo…

逆概率加权(R和Python案例)

逆概率加权&#xff08;Inverse Probability Weighting, IPW&#xff09;是一种统计技术&#xff0c;用于观察性研究中调整混杂变量的影响&#xff0c;以便更准确地估计因果关系。这种方法特别有用于在无法进行随机化实验的情况下&#xff0c;通过给予不同个体不同的权重&#…

[数据库实验七]事务设计

目录 一、实验目的与要求 二、实验内容 三、实验小结 实验中涉及的数据参考&#xff1a;[数据库实验一]数据库和表-CSDN博客 一、实验目的与要求 1.熟悉提交事务 2.回滚事务 3.检查点技术 注&#xff1a;可以用可视化软件来实现 二、实验内容 基于现有数据库设计事务提…

Axure-本地发布,局域网内用户访问

目录 前言内容一. 选择Axure发布路径&#xff0c;进行相应配置二.添加IIS服务配置&#xff08;不在这里赘述&#xff01;&#xff09;三&#xff1a;添加IIS配置&#xff0c;创建网站四.添加安全策略&#xff1a;实践&#xff1a; 前言 最近加入了公司的积分系统&#xff0c;由…

时尚与科技的融合,戴上更轻更悦耳的QCY C30耳夹耳机,随时享受好音乐

不知不觉&#xff0c;蓝牙耳机已经成了我日常必备的小工具了&#xff0c;尤其是这两年新出现的耳夹式耳机&#xff0c;轻松解决了入耳式耳机的舒适性难题&#xff0c;戴上一整天也不会觉得累&#xff0c;而且一款好看的耳机不仅能够提供澎湃的音质&#xff0c;还能成为时尚出街…

理解信息安全中的SOAR

在信息安全领域&#xff0c;SOAR&#xff08;Security Orchestration, Automation, and Response&#xff0c;安全编排、自动化与响应&#xff09;是一个现代化的解决方案&#xff0c;旨在通过集成和自动化的方式优化安全操作流程&#xff0c;提升威胁检测、事件响应的速度与效…

雷达原理-绪论-西电魏青

Radar 雷达&#xff1a;radio detection and ranging 无线电探测和测距 无线电&#xff1a;电磁波信号 探测&#xff1a;通过电磁波信号的一个回波特性实现物体探测 测距&#xff1a;利用回波信号&#xff08;现在技术发展&#xff0c;不单单是测距&#xff0c;还能是物体的各种…

stm32 flash无法擦除

通过bushound调试代码发现&#xff0c;当上位机发送命令到模组后flash将不能擦除&#xff0c;通过 HAL_FLASH_GetError&#xff08;&#xff09;函数查找原因是FLASH Programming Sequence error&#xff08;编程顺序错误&#xff09;&#xff0c;解决办法是在解锁后清零标志位…

构建高效房屋租赁系统:Spring Boot应用

1 绪论 1.1 研究背景 中国的科技的不断进步&#xff0c;计算机发展也慢慢的越来越成熟&#xff0c;人们对计算机也是越来越更加的依赖&#xff0c;科研、教育慢慢用于计算机进行管理。从第一台计算机的产生&#xff0c;到现在计算机已经发展到我们无法想象。给我们的生活改变很…

MySQL purge逻辑分析与调测

目录 purge逻辑分析与调测 一、purge线程 1、什么是purge线程&#xff1f; 2、为什么需要purge&#xff1f; 3、purge哪些内容&#xff1f; 4、什么时候purge&#xff1f; 5、purge内存结构 二、purge主要步骤 1、确认可见性 2、获取需要purge的undo记录 …

开源鸿蒙OpenHarmony系统修改屏幕旋转方法 深圳触觉智能RK3566开发板

本文提供OpenHarmony4.0系统竖屏修改为旋转时的操作方法&#xff0c;在此为大家介绍了两种方法&#xff01;第一种方式是将修改过的文件重新编译后烧录至开发板中实现屏幕旋转&#xff0c;第二种方式则是通过覆盖原文件的方式实现屏幕旋转。 1、源码下载 首先进入触觉智能官网…

【C语言从不挂科到高绩点】22-指针04-结构体指针【重点知识】

Hello!彦祖们,俺又回来了!!!,继续给大家分享 《C语言从不挂科到高绩点》课程!! 本节将为大家讲解C语言中非常重要的知识点-指针: 本套课程将会从0基础讲解C语言核心技术,适合人群: 大学中开设了C语言课程的同学想要专升本或者考研的同学想要考计算机等级证书的同学想…

UART开发基础

目录 前言 同步传输与异步传输 1.概念与示例 2.差别 UART协议与操作方法 1.UART协议 2.STM32H5 UART硬件结构 3.RS485协议 UART编程 1.三种编程方式 2.查询方式 3.中断方式 4.DMA 方式 效率最高的UART编程方法 1.IDLE中断 2.DMA 发送/DMAIDLE 接收 在RTOS里使…

L2 Loss介绍及梯度计算说明

L1 Loss介绍及梯度计算说明-CSDN博客 L2 Loss&#xff08;MS&#xff0c;也称为均方误差损失或平方误差损失&#xff09;是一种常用的损失函数&#xff0c;广泛应用于回归任务中。它通过计算预测值与真实值之间的平方差来评估模型的性能。 1. L2 Loss 的定义 L2 Loss 的数…

Python | Leetcode Python题解之第437题路径总和III

题目&#xff1a; 题解&#xff1a; class Solution:def pathSum(self, root: TreeNode, targetSum: int) -> int:prefix collections.defaultdict(int)prefix[0] 1def dfs(root, curr):if not root:return 0ret 0curr root.valret prefix[curr - targetSum]prefix[cu…

Golang | Leetcode Golang题解之第436题寻找右区间

题目&#xff1a; 题解&#xff1a; func findRightInterval(intervals [][]int) []int {n : len(intervals)type pair struct{ x, i int }starts : make([]pair, n)ends : make([]pair, n)for i, p : range intervals {starts[i] pair{p[0], i}ends[i] pair{p[1], i}}sort.…

第四届工业母机高质量发展论坛在浙江温岭召开

9月24日&#xff0c;由工业和信息化部产业发展促进中心&#xff08;以下简称产促中心&#xff09;主办的“第四届工业母机高质量发展论坛”在浙江温岭成功召开。 中国工程院院士周济、郭东明、王国庆&#xff0c;工业和信息化部装备工业一司一级巡视员苗长兴&#xff0c;中国企…

C语言 | Leetcode C语言题解之第437题路径总和III

题目&#xff1a; 题解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ //递归遍历树节点&#xff0c;判断是否为有效路径 int dfs(struct TreeNode * root, int ta…

C++ -函数重载-详解

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【C】 欢迎点赞&#x1f44d;收藏⭐关注❤️ C -函数重载-详解 1.是什么2.怎么用2.1示例 3.原理3.1C/C编译链接过程3.2函数名修饰规则3.3过程1.调用函数的过程2.编译阶段的函数调用 总结 1.是什么 如果在百度中搜索重载这…