【C++进阶】二叉搜索树

news2024/11/16 3:50:12

文章目录

  • 二叉搜索树概念
  • 二叉搜索树操作
  • 二叉搜索树的实现
    • 每个节点的结构
    • 插入
    • 查找
    • 删除
  • 二叉搜索树的所有代码(包括测试)
    • 版本一
    • 版本二
    • test.cpp

二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
在这里插入图片描述

二叉搜索树操作

在这里插入图片描述

  1. 二叉搜索树的查找
    a、从根开始比较,查找,比根大往右边走查找,比根小往左边走查找。
    b、最多查找高度次,走到到空,还没找到,这个值不存在。
  2. 二叉搜索树的插入
    插入的具体过程如下:
    a. 树为空,则直接新增节点,赋值给root指针
    b. 树不空按二叉搜索树性质查找插入位置,插入新节点
  3. 二叉搜索树的删除
    首先查找元素是否在二叉搜索树中,如果不在,则返回, 否则要删除的结点可能分下面四种情况:
    a. 要删除的结点无孩子结点
    b. 要删除的结点只有左孩子结点
    c. 要删除的结点只有右孩子结点
    d. 要删除的结点有左、右孩子结点

    看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
    如下:
    情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
    情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
    情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除

二叉搜索树的实现

每个节点的结构

在这里插入图片描述

插入

在这里插入图片描述

查找

在这里插入图片描述

删除

在这里插入图片描述
因为代码太长,无法画图,那么注释分析一下。
在这里插入图片描述

二叉搜索树的所有代码(包括测试)

版本一

#pragma once
namespace lzf
{
	template<class K>
	struct BinarySearchTreeNode
	{
		K _key;
		BinarySearchTreeNode<K>* left;
		BinarySearchTreeNode<K>* right;

		BinarySearchTreeNode(const K& key)
			:_key(key)
			,left(nullptr)
			,right(nullptr)
		{}
	};

	template<class K>
	class BinarySearchTree
	{
	public:
		typedef BinarySearchTreeNode<K> Node;
		BinarySearchTree()
			:_root(nullptr)
		{}
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* prev = nullptr;
			Node* cur = _root;
			
			while (cur)
			{
				if (cur->_key > key)
				{
					prev = cur;
					cur = cur->left;
				}
				else if(cur -> _key < key)
				{
					prev = cur;
					cur = cur->right;
				}
				else
				{
					return false;
				}
			}

			if (prev->_key > key)
			{
				prev->left = new Node(key);
			}
			else
			{
				prev->right = new Node(key);
			}
			return true;

		}
		bool Find(const K& key)
		{
			if (_root == nullptr)
				return false;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					cur = cur->left;
				}
				else if (cur->_key < key)
				{
					cur = cur->right;
				}
				else
				{
					return true;
				}
			}

			return false;
		}
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
		bool Erase(const K& key)
		{
			if (_root == nullptr)
				return false;
			Node* cur = _root;
			Node* prev = _root;
			//cur为空条件结束,说明没有找到要删除的值
			while (cur)
			{
				//如果要删除值,比该节点的值小
				if (cur->_key > key)
				{
					//记录父母
					prev = cur;
					//往左路再找
					cur = cur->left;
				} 
				//如果要删除值,比该节点的值小
				else if (cur->_key < key)
				{
					//记录父母
					prev = cur;
					//往左路再找
					cur = cur->right;
				}
				else
				{
					//找到了
					//该节点左孩子或者有右孩子为空的情况又或者全部为空的情况
					if (cur->left == nullptr || cur->right == nullptr)
					{
						//如果要删除的就是根节点
						if (_root == cur)
						{
							//如果左孩子不为空,把左孩子变为根
							if (cur->left == nullptr)
							{
								_root = cur->right;
							}
							//如果右孩子不为空,把右孩子变为根
							else
							{
								_root = cur->left;
							}
						}
						//如果要删除的是根节点以外的节点
						else
						{
							//如果该节点是父母节点的左孩子
							if (prev->left == cur)
							{
								//如果该节点的左孩子不为空,直接让父母指向该节点的左孩子
								if (cur->left == nullptr)
								{
									prev->left = cur->right;
								}
								//否则,直接让父母指向该节点的右孩子
								else
								{
									prev->left = cur->left;
								}
							}
							//如果该节点是父母节点的右孩子
							else
							{
								//如果该节点的左孩子不为空,直接让父母指向该节点的左孩子
								if (cur->left == nullptr)
								{
									prev->right = cur->right;
								}
								//否则,直接让父母指向该节点的右孩子
								else
								{
									prev->right = cur->left;
								}
							}
							//删除该节点,并返回删除成功
							delete cur;
							return true;
						}
					}
					//该节点的左右孩子均存在
					else
					{
						//记录根的左子树最小值节点
						Node* min = _root->right;
						//记录根的左子树最小值节点的父母
						Node* minprev = _root;

						//从根的右子树开始,寻找最小值节点
						while (min->left)
						{
							minprev = min;
							min = min->left;
						}
						//找到了,交换两节点的值
						swap(cur->_key, min->_key);

						//判断该节点是父母节点的左孩子还是右孩子,然后把该节点的右孩子给父母
						//记住这里的min一定只有右孩子,因为它的最小值
						if (minprev->left == min)
							minprev->left = min->right;
						else
							minprev->right = min->right;

						//删除该节点,并返回删除成功
						delete min;
						return true;
					}
				}
			}

			//走到这里说明没有找到要删除的值,直接返回false
			return false;
		}

		bool InsertR(const K& key)
		{
			return _InserR(_root, key);
		}
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->left);
			cout << root->_key;
			_InOrder(root->right);
		}
		bool _InserR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}

			if (root->_key < key)
			{
				return _InserR(root->right, key);
			}
			else if (root->_key > key)
			{
				return _InserR(root->left, key);
			}
			else
			{
				return false;
			}
		}
		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}

			if (root->_key < key)
			{
				return _FindR(root->right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->left, key);
			}
			else
			{
				return true;
			}
		}
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
				return false;
			if (root->_key > key)
			{
				return _EraseR(root->left, key);
			}
			else if (root->_key < key)
			{
				return _EraseR(root->right, key);
			}
			else
			{
				Node* del = root;
				if (root->left == nullptr)
				{
					root = root->right;
				}
				else if (root->right == nullptr)
				{
					root = root->left;
				}
				else
				{
					Node* min = root->right;
					while (min->left)
					{
						min = min->left;
					}
					swap(root->_key, min->_key);

					return _EraseR(root->right, key);
				}

				delete del;
				return true;
			}
		}
	private:
		Node* _root;
	};

	void TestBinarySearchTree1()
	{
		BinarySearchTree<int> t;
		int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
		for (auto e : a)
		{
			t.InsertR(e);
		}
		t.InOrder();
		/*cout << t.Find(8) << endl;
		cout << t.Find(9) << endl;
		cout << t.Find(10) << endl;*/
		/*t.Erase(4);
		t.InOrder();
		t.Erase(7);
		t.InOrder();
		t.Erase(5);
		t.InOrder();
		t.Erase(0);
		t.InOrder();
		t.Erase(1);
		t.InOrder();
		t.Erase(2);
		t.InOrder();
		t.Erase(3);
		t.InOrder();
		t.Erase(6);
		t.InOrder(); 
		t.Erase(8);
		t.InOrder();
		t.Erase(9);
		t.InOrder();*/
		/*cout << t.Find(8) << endl;
		cout << t.Find(9) << endl;
		cout << t.Find(10) << endl;*/
		t.EraseR(4);
		t.InOrder();
		t.EraseR(7);
		t.InOrder();
		t.EraseR(5);
		t.InOrder();
		t.EraseR(0);
		t.InOrder();
		t.EraseR(1);
		t.InOrder();
		t.EraseR(2);
		t.InOrder();
		t.EraseR(3);
		t.InOrder();
		t.EraseR(6);
		t.InOrder();
		t.EraseR(8);
		t.InOrder();
		t.EraseR(9);
		t.InOrder();
	}
}

版本二

namespace lzh
{
	template<class K,class V>
	struct BinarySearchTreeNode
	{
		K _key;
		V _value;
		BinarySearchTreeNode<K,V>* left;
		BinarySearchTreeNode<K,V>* right;

		BinarySearchTreeNode(const K& key,const V& value)
			:_key(key)
			,_value(value)
			, left(nullptr)
			, right(nullptr)
		{}
	};

	template<class K,class V>
	class BinarySearchTree
	{
	public:
		typedef BinarySearchTreeNode<K,V> Node;
		BinarySearchTree()
			:_root(nullptr)
		{}
		
		
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
		

		bool InsertR(const K& key,const V& value)
		{
			return _InserR(_root, key, value);
		}
		Node* FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->left);
			cout << root->_key << " " << root->_value << endl;
			_InOrder(root->right);
		}
		bool _InserR(Node*& root, const K& key, const V& value)
		{
			if (root == nullptr)
			{
				root = new Node(key, value);
				return true;
			}

			if (root->_key < key)
			{
				return _InserR(root->right, key, value);
			}
			else if (root->_key > key)
			{
				return _InserR(root->left, key, value);
			}
			else
			{
				return false;
			}
		}
		Node* _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
			{
				return nullptr;
			}

			if (root->_key < key)
			{
				return _FindR(root->right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->left, key);
			}
			else
			{
				return root;
			}
		}
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
				return false;
			if (root->_key > key)
			{
				return _EraseR(root->left, key);
			}
			else if (root->_key < key)
			{
				return _EraseR(root->right, key);
			}
			else
			{
				Node* del = root;
				if (root->left == nullptr)
				{
					root = root->right;
				}
				else if (root->right == nullptr)
				{
					root = root->left;
				}
				else
				{
					Node* min = root->right;
					while (min->left)
					{
						min = min->left;
					}
					swap(root->_key, min->_key);

					return _EraseR(root->right, key);
				}

				delete del;
				return true;
			}
		}
	private:
		Node* _root;
	};
	void Test_BinarySearchTree1()
	{
		// 字典KV模型
		BinarySearchTree<string, string> dict;
		dict.InsertR("sort", "排序");
		dict.InsertR("left", "左边");
		dict.InsertR("right", "右边");
		dict.InsertR("map", "地图、映射");
		//...
		dict.InOrder();
		dict.EraseR("left");
		dict.EraseR("sort");
		dict.EraseR("right");
		dict.EraseR("map");
		string str;
		while (cin >> str)
		{
			BinarySearchTreeNode<string, string>* ret = dict.FindR(str);
			if (ret)
			{
				cout << "对应中文解释:" << ret->_value << endl;
			}
			else
			{
				cout << "无此单词" << endl;
			}
		}
	}
	void Test_BinarySearchTree2()
	{
		// 统计水果出现次数
		string arr[] = { "苹果", "西瓜","草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		BinarySearchTree<string, int> countTree;
		for (auto& str : arr)
		{
			//BSTreeNode<string, int>* ret = countTree.Find(str);
			auto ret = countTree.FindR(str);
			if (ret != nullptr)
			{
				ret->_value++;
			}
			else
			{
				countTree.InsertR(str, 1);
			}
		}

		countTree.InOrder();
	}
}

test.cpp

#include<iostream>
using namespace std;
#include"BinarySearchTree.h"
int main()
{
	//lzf::TestBinarySearchTree1();
	//lzh::Test_BinarySearchTree1();
	lzh::Test_BinarySearchTree2();
	return 0;
}

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

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

相关文章

Linux环境下gdb程序调试

目录gdb介绍进入gdb调试环境指令学习l(list)指令b(break)指令info b指令d指令r(run)指令n(next)指令s(step)指令c(continue)指令bt(breaktrace)指令finish指令p(print)指令display指令undisplay指令until指令disable命令enable命令这篇文章将会介绍gdb以及一些常用的gdb调试指令…

3.移动端百分比布局练习-京东首页

访问地址 https://youthddup.gitee.io/myproject/ 1、项目目录结构 2、注意 &#xff08;1&#xff09;设置视口标签以及引入初始化样式 &#xff08;2&#xff09;二倍精灵图缩放 先把精灵图等比缩放原来的一半 然后再测精灵图位置 代码里background-size置为原来的一半 &a…

typescript 数组操作

使用变量来存储值会带来以下限制&#xff1a; 变量本质上是标量。换言之&#xff0c;一个变量声明变量声明一次只能包含一个。这意味着在程序中存储n个值需要n个变量声明。因此&#xff0c;当需要存储更大的值集合时&#xff0c;使用变量是不可行的。 程序中的变量以随机顺序分…

当下一场数字化的浪潮,正在各行各业深刻上演着

一场数字化的浪潮&#xff0c;正在各行各业深刻上演着。在零售领域&#xff0c;亦不例外。以往&#xff0c;提及零售&#xff0c;我们更多地想到的是&#xff0c;各式各样的电商平台&#xff0c;我们看到的是&#xff0c;各式各样的电商模式&#xff1b;现在&#xff0c;提及零…

测试用例设计-淘宝购物车

测试人员和开发人员产生争执了怎么办&#xff1f; ① 先检查自身&#xff0c;是否BUG描述不清楚 ② 站在用户的角度考虑问题 ③ BUG定级要有理有据 ④ 提高自身的技术能力和业务水平&#xff0c;最好同时提出解决方案。 ⑤ 开发人员不接受时&#xff0c;不要争吵&#xff0c;可…

YOLOv5更换骨干网络之 PP-LCNet

论文地址&#xff1a;https://arxiv.org/abs/2109.15099 代码地址&#xff1a;https://github.com/ngnquan/PP-LCNet 我们提出了一种基于MKLDNN加速策略的轻量级CPU网络&#xff0c;名为PP LCNet&#xff0c;它提高了轻量级模型在多个任务上的性能。本文列出了在延迟几乎不变的…

YOLOv5更换骨干网络之 EfficientNet-B0

论文地址&#xff1a;https://arxiv.org/abs/1905.11946 代码地址&#xff1a;https&#xff1a;//githeb.com/TensorFlow/tpu/tree/master/Models/Offical/Efficientnet 卷积神经网络(ConvNet)通常是在固定的资源预算下开发的&#xff0c;如果有更多的资源可用&#xff0c;则…

如何从macOS ventura降级到 macOS Monterey?这两大方法可以帮到你

苹果发布了macOS 13 Ventura的正式版系统&#xff0c;增加了许多实用性的功能&#xff0c;大家纷纷下载更新最新版本的系统。但根据许多已安装ventura的用户反馈&#xff0c;这个版本的MacOS系统还不够成熟&#xff0c;应该有不少bug还没有修复过来&#xff0c;从而求助小编分享…

c#入门-泛型约束

泛型约束 使用泛型时会假设泛型占位符是任何类型。 但因为它被假设是任何类型&#xff0c;所以使用起来有很大的限制。只有所有类型都有的功能&#xff0c;他才能用。 为了满足所有的可能类型&#xff0c;可用的操作非常少。 为此我们可以为泛型占位符添加约束。虽然会让能兼…

大型项目迭代流程

一、回顾目标 总目标&#xff1a; 年底上线完成100% 结果&#xff1a; 年底上线并开量验证过成功&#xff0c;完成率100% 阶段目标A&#xff1a; 10月底项目全流程开发完成&#xff0c;并提测出票前流程 结果&#xff1a;10月21日项目开发完成100%&#xff0c;10月25日前…

基于残差神经网络的交通标志识别算法研究与应用实现

问题&#xff1a; 从图像中识别交通标志对于自动驾驶至关重要。要想实现自动驾驶&#xff0c;车辆必须了解并遵守所有交通规则。当前&#xff0c;特斯拉、谷歌、梅赛德斯-奔驰、丰田、福特、奥迪等许多大公司都在研究自动驾驶。因此&#xff0c;为了实现这项技术的准确性&…

pandas的series创建和pandans的dataFrame创建

一&#xff1a;series和读取外部数据 1.1pandas的series的了解 1.1.1 为什么要学习pandas numpy能够帮我们处理处理数值型数据&#xff0c;但是这还不够。很多时候&#xff0c;我们的数据除了数值之外&#xff0c;还有字符串&#xff0c;还有时间序列等 比如&#xff1a;我们通…

显式利用用户画像的多兴趣建模

显式利用用户画像的多兴趣建模 目前在多兴趣建模中&#xff0c;用户侧的特征包括用户基础画像特征&#xff08;年龄、性别、地域等&#xff09;、用户在当前场景的静态兴趣画像特征&#xff08;短期兴趣画像、长期兴趣画像&#xff09;、交互的历史正向行为序列特征&#xff0…

【Javassist】快速入门系列13 使用Javassist获取注解

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 04 使用Javassist更改整个方法体 05 当有指定方法调用时替换方法调用的内容 06 当有构造方法调用时替换方法调用的内容 07 当检测到字段被访问时使用语…

MySQL性能优化三 一条SQL在MySQL中执行的过程

一 MySQL的内部组件结构 大体来说&#xff0c;MySQL 可以分为 Server 层和存储引擎层两部分。 1.1 service层 主要包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有的内置函数&#xff08;如日期、时间、数学…

Easy-Captcha验证码 生成以及校验(简单易懂)

目录说明pom引入详解参数类使用easy-captcha 中提供了下面几种类源码说明Captcha使用验证图解源码测试GitHub说明 Java图形验证码&#xff0c;支持gif、中文、算术等类型&#xff0c;可用于Java Web、JavaSE等项目 pom引入 <dependency><groupId>com.github.whvc…

【C++】 bitset(位图)的使用

目录 一、bitset的基本介绍 1. 位图的概念 2. 位图的应用 二、biset的基本使用 1. bitset的成员函数 2. 基本使用介绍 1. 定义方式 2. 成员函数的使用 一、bitset的基本介绍 1. 位图的概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数…

win系统一台电脑安装两个不同版本的mysql教程

1.mysql下载zip包&#xff08;地址&#xff09;MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ 2.解压在你的电脑上&#xff08;不要再C盘和带中文的路径&#xff09; data和my.ini是没有的。 3.创建my.ini文件 创建记事本改变后缀名就可以 …

【5G RRC】小区搜索(Cell Search)和系统捕获(System Acquisition)流程

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

环境变量?拿来把你!

文章目录环境变量直接运行程序的第一种方法&#xff1a;把程序移动到系统目录底下echo $环境变量&#xff1a;查看环境变量PATH:指定命令的搜索路径export 定义一个新的环境变量export PATH旧路径&#xff1a;新路径getenv&#xff1a;获取环境变量—获取环境变量的第一种方式s…