二叉搜索树/二叉排序树/二叉查找树

news2024/12/30 2:18:56

文章目录

  • 1.概念
  • 2.操作
  • 3.实现
    • 3.1框架
    • 3.2BSTree.h
    • 3.3test.cpp

在这里插入图片描述

1.概念

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

2.操作

  1. 二叉搜索树的查找
    a、从根开始,查找,比根大往右查,比根小往左查。
    b、最多查找高度次,走到空还没找到,这个值不存在。
  2. 二叉搜索树的插入
    插入的具体过程如下:
    a. 树为空,新增节点,赋值给root指针
    b. 树不空,查找插入位置,插入新节点
    3.二叉搜索树的删除
    查找元素是否在二叉搜索树中,不存在返回false。
    存在:目标结点
    a. 无子结点
    b. 只有左子结点
    c. 只有右子结点
    d. 有左、右子结点
    情况b:删除目标结点且使目标结点的父结点指向目标结点的左子结点–直接删除
    情况c:删除目标结点且使目标结点的父结点指向目标结点的右子结点–直接删除
    情况d:在目标结点的右子树中寻找中序下的第一个最小结点,用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除

3.实现

3.1框架

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

3.2BSTree.h

#pragma once

template<class K>
//树结点
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{
	
	}
};
//二叉搜索树
//K模型
namespace K 
{
	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	public:
		//构造函数
		/*
		BSTree()
			:_root(nullptr)
		{

		}
		*/
		BSTree() = default; // 制定强制生成默认构造
		//拷贝构造
		BSTree(const BSTree<K>& t)
		{
			_root = Copy(t._root);
		}
		//赋值重载
		BSTree<K>& operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}
		//析构函数
		~BSTree()
		{
			Destroy(_root);
		}
		//插入 
		bool Insert(const K& 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;
		}
		//插入-递归版
		bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}
		//查找
		bool 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 true;
				}
			}

			return false;
		}
		//查找-递归版
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		//删除
		bool Erase(const K& key)
		{
			Node* cur = _root;
			Node* parent = nullptr;

			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				// 找到目标结点--删除
				else
				{
					// 1、左为空
					if (cur->_left == nullptr)
					{
						//目标结点为根节点
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							//目标结点为左结点
							if (parent->_left == cur)
							{
								//父左接管目标右
								parent->_left = cur->_right;
							}
							//目标结点为右结点
							else
							{
								//父右接管目标右
								parent->_right = cur->_right;
							}
						}
						delete cur;
					}
					// 2、右为空
					else if (cur->_right == nullptr)
					{
						//目标结点为根节点
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							//父左接管目标左
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							//父右接管目标左
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
					}
					// 3、左右均有子结点
					else
					{
						//找右树最小节点/左树最大节点
						//一、右树最小节点
						//obj只能是 只有右节点或叶子 
						//找obj【右树最小结点】
						//Node* nannyDad = cur;
						//Node* nanny = cur->_right;
						//while (nanny->_left)
						//{
						//	nannyDad = nanny;
						//	nanny = nanny->_left;
						//}
						赋值
						//cur->_key = nanny->_key;
						obj在父左 obj的右给父左
						//if (nannyDad->_left == nanny)
						//{
						//	nannyDad->_left = nanny->_right;
						//}
						obj在父右 obj的右给父右
						//else
						//{
						//	nannyDad->_right = nanny->_right;
						//}
						//delete nanny;
						//二、左树最大结点
						//obj只能是 只有左节点或叶子 
						//找obj【左树最大结点】
						Node* nannyDad = cur;
						Node* nanny = cur->_left;
						while (nanny->_right)
						{
							nannyDad = nanny;
							nanny = nanny->_right;
						}
						//赋值
						cur->_key = nanny->_key;
						//obj在父左 obj的右给父左
						if (nannyDad->_left == nanny)
						{
							nannyDad->_left = nanny->_right;
						}
						//obj在父右 obj的右给父右
						else
						{
							nannyDad->_right = nanny->_right;
						}
						delete nanny;
					}

					return true;
				}
			}

			return false;
		}
		//删除-递归版
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
		//中序遍历
		//1.直接写调用时传_root传不过去 _root是private [可以用GetRoot()]
		//2._root又无法做缺省值:条件是全局变量 常量 静态变量 且无this指针【this->_root】
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
	protected:

		//拷贝函数
		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 Destroy(Node*& root)
		{
			if (root == nullptr)
				return;

			Destroy(root->_left);
			Destroy(root->_right);

			delete root;
			root = nullptr;
		}
		//查找-递归版
		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key == key)
				return true;

			if (root->_key < key)
				return _FindR(root->_right, key);
			else
				return _FindR(root->_left, key);
		}
		//插入-递归版
		bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				//引用传参 root是上层root的left或right 直接链接 yyds
				root = new Node(key);
				return true;
			}

			if (root->_key < key)
			{
				return _InsertR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			else
			{
				return false;
			}
		}
		//删除-递归版
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			//相等--删除
			else
			{
				//记录root 
				Node* obj = root;
				//右为空
				if (root->_right == nullptr)
				{
					root = root->_left; //实际是上层的root与当前层的root左链接 下同
				}
				//左为空
				else if (root->_left == nullptr)
				{
					root = root->_right;
				}
				//左右都不空
				else
				{
					Node* nanny = root->_left;
					while (nanny->_right)
					{
						nanny = nanny->_right;
					}
					swap(root->_key, nanny->_key);
					//将找到的nanny与obj交换 然后传新树递归删除obj
					//【此时obj定为叶子节点-利用递归将删除一个有左右子结点的结点转换为删除一个叶子节点】
					return _EraseR(root->_left, key);
				}
				delete obj;
				return true;
			}
		}
		//中序遍历 
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;

			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}
	private:
		Node* _root = nullptr;
	};
}
//KV模型
namespace KV
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;
		K _key;
		V _value;

		//构造函数
		BSTreeNode(const K& key, const V& value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{
		
		}
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		//插入
		bool Insert(const K& 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 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;
		}
		//删除
		bool Erase(const K& key)
		{
			Node* cur = _root;
			Node* parent = nullptr;

			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				// 找到目标结点--删除
				else
				{
					// 1、左为空
					if (cur->_left == nullptr)
					{
						//目标结点为根节点
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							//目标结点为左结点
							if (parent->_left == cur)
							{
								//父左接管目标右
								parent->_left = cur->_right;
							}
							//目标结点为右结点
							else
							{
								//父右接管目标右
								parent->_right = cur->_right;
							}
						}
						delete cur;
					}
					// 2、右为空
					else if (cur->_right == nullptr)
					{
						//目标结点为根节点
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							//父左接管目标左
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							//父右接管目标左
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
					}
					// 3、左右均有子结点
					else
					{
						//找右树最小节点/左树最大节点
						//一、右树最小节点
						//obj只能是 只有右节点或叶子 
						//找obj【右树最小结点】
						//Node* nannyDad = cur;
						//Node* nanny = cur->_right;
						//while (nanny->_left)
						//{
						//	nannyDad = nanny;
						//	nanny = nanny->_left;
						//}
						赋值
						//cur->_key = nanny->_key;
						obj在父左 obj的右给父左
						//if (nannyDad->_left == nanny)
						//{
						//	nannyDad->_left = nanny->_right;
						//}
						obj在父右 obj的右给父右
						//else
						//{
						//	nannyDad->_right = nanny->_right;
						//}
						//delete nanny;
						//二、左树最大结点
						//obj只能是 只有左节点或叶子 
						//找obj【左树最大结点】
						Node* nannyDad = cur;
						Node* nanny = cur->_left;
						while (nanny->_right)
						{
							nannyDad = nanny;
							nanny = nanny->_right;
						}
						//赋值
						cur->_key = nanny->_key;
						//obj在父左 obj的右给父左
						if (nannyDad->_left == nanny)
						{
							nannyDad->_left = nanny->_right;
						}
						//obj在父右 obj的右给父右
						else
						{
							nannyDad->_right = nanny->_right;
						}
						delete nanny;
					}
					return true;
				}
			}
			return false;
		}

		//中序遍历
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

protected:
	//中序遍历
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

3.3test.cpp

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
using namespace std;

#include "BSTree.h"

//插入、遍历、删除
void TestBSTree()
{
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	K::BSTree<int> t1;
	//插入建树
	for (auto e : a)
	{
		t1.Insert(e);
	}

	t1.InOrder();

	t1.Erase(4);
	t1.InOrder();

	t1.Erase(14);
	t1.InOrder();

	t1.Erase(3);
	t1.InOrder();

	t1.Erase(8);
	t1.InOrder();
}
//KV模型--简易字典
void TestBSTree2()
{
	KV::BSTree<string, string> Dictionary;
	Dictionary.Insert("sort", "排序");
	Dictionary.Insert("left", "左边");
	Dictionary.Insert("right", "右边");
	Dictionary.Insert("string", "字符串");
	Dictionary.Insert("insert", "插入");
	Dictionary.Insert("erase", "删除");

	string str;
	while (cin >> str)
	{
		auto ret = Dictionary.Find(str);
		if (ret)
		{
			cout <<str << ":" << ret->_value << endl;
		}
		else
		{
			cout << "无此单词" << endl;
		}
	}
}
//KV模型--水果统计树
void TestBSTree3()
{
	string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };

	KV::BSTree<string, int> countTree;
	for (auto str : arr)
	{
		//KV::BSTreeNode<string, int>* 
		auto ret = countTree.Find(str);
		if (ret == nullptr)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_value++;
		}
	}

	countTree.InOrder();
}

int main()
{
	TestBSTree3();
	return 0;
}

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

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

相关文章

python 学习笔记(5)——SMTP 使用QQ邮箱发送邮件

目录 发送邮件 1、准备工作&#xff1a; 2、发送纯文本信息内容&#xff1a; 3、发送 HTML 格式的内容&#xff1a; 4、发送带附件的邮件&#xff1a; 5、群发&#xff08;一个邮件&#xff0c;发给多个人&#xff09;&#xff1a; 发送邮件 以下都 以 QQ邮箱 为发送方举…

敏捷开发方法管理项目,适应变化,引领未来

​敏捷开发方法是一种灵活且高效的项目管理方法&#xff0c;旨在应对不断变化的需求和快速发展的项目环境。使用敏捷开发方法可以帮助团队更好地应对不确定性&#xff0c;提高项目的质量和效率。以下是使用敏捷开发方法管理项目的具体步骤&#xff1a; 明确项目目标和范围 在…

算法通过村第六关-树白银笔记|层次遍历

文章目录 前言1. 层次遍历介绍2. 基本的层次遍历与变换2.1 二叉树的层次遍历2.2 层次遍历-自底向上2.3 二叉树的锯齿形层次遍历2.4 N叉树的层次遍历 3. 几个处理每层元素的题目3.1 在每棵树行中找出最大值3.2 在每棵树行中找出平均值3.3 二叉树的右视图3.4 最底层最左边 总结 前…

C高级day4(shell脚本)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 1.创建一个文件&#xff0c;给组用户可读权限&#xff0c;所属用户可写权限&#xff0c;其他用户可执行权限&#xff0c;使用if判断文件有哪些权限 #!/bin/bash touch 1 chmod 241 1 if [ -r 1 ] thenecho "文件…

为 DevOps 战士准备的 Linux 命令

点击链接了解详情 这篇文章将帮助理解DevOps工程师所需的大部分重要且经常使用的Linux命令。 要执行这些命令&#xff0c;你可以使用任何Linux机器、虚拟机或在线Linux终端来迅速开始使用这些命令。 系统信息命令&#xff1a; hostname - 显示系统主机的名称。 hostid - 显示…

openGauss学习笔记-66 openGauss 数据库管理-创建和管理schema

文章目录 openGauss学习笔记-66 openGauss 数据库管理-创建和管理schema66.1 背景信息66.2 注意事项66.3 操作步骤66.3.1 创建管理用户及权限schema66.3.2 使用schema66.3.3 schema的搜索路径66.3.4 schema的权限控制66.3.5 删除schema openGauss学习笔记-66 openGauss 数据库管…

Codeforces Round 827 (Div. 4) D 1e5+双重for循环技巧

Codeforces Round 827 (Div. 4) D 做题链接&#xff1a;Codeforces Round 827 (Div. 4) 给定一个由 n个正整数 a1,a2,…,an&#xff08;1≤ai≤1000&#xff09;组成的数组。求ij的最大值&#xff0c;使得ai和aj共质&#xff0c;否则−1&#xff0c;如果不存在这样的i&#…

github 创建自己的分支 并下载代码

github创建自己的分支 并下载代码 目录概述需求&#xff1a; 设计思路实现思路分析1.进入到master分支&#xff0c;git checkout master;2.master-slave的个人远程仓库3.爬虫调度器4.建立本地分支与个人远程分支之间的联系5.master 拓展实现 参考资料和推荐阅读 Survive by day…

Python基于Flask的招聘信息爬取、招聘信息可视化系统

招聘信息可视化系统 一、介绍 此系统是一个实时分析招聘信息的系统&#xff0c;应用Python爬虫、Flask框架、Echarts、VUE等技术实现。 二、系统运行图 1、数据概览 将爬取到的数据进行展示&#xff0c;点击公司信息和职位信息可以跳转到相应的网址&#xff0c;支持多条件…

这一次,我顿悟了

大家好&#xff0c;我是苍何。昨晚和编程导航 星球嘉宾也是我的引路人闫&#xff08;yn&#xff09; 小林大佬&#xff0c;畅聊了 4 个 小时&#xff0c;至今内心还是久久不能平静。 小林和我一样是跨界转行&#xff0c;他是医学院毕业&#xff0c;大二开始自学编程&#xff0…

【分布式】分布式事务:2PC

分布式事务的问题可以分为两部分&#xff1a; 并发控制 concurrency control原子提交 atomic commit 分布式事务问题的产生场景&#xff1a;一份数据被分片存在多台服务器上&#xff0c;那么每次事务处理都涉及到了多台机器。 可序列化&#xff08;并发控制&#xff09;&…

软件设计师学习笔记10-死锁资源数计算+进程资源图+段页式存储

目录 1.死锁资源数计算 1.1死锁 1.2进程管理与死锁资源的计算 2.进程资源图 3.段页式存储 3.1页式存储 3.1.1页式存储组织 3.1.2完整页表及页面淘汰原则 3.1.3页面置换算法(了解一下) 3.2段式存储 1.死锁资源数计算 1.1死锁 (1)死锁的概念&#xff1a;所谓死锁&…

C++-day4

仿照string类&#xff0c;完成myString 类 #include <iostream> #include <cstring> using namespace std; class myString { private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度 public://无参构造myString():size(10…

mac 13.x 打开第三方应用,提示已损坏无法打开

前排提示&#xff0c;不一定有效 1、先在终端执行下面这个&#xff0c;因为要提权&#xff0c;输入自己的密码 sudo xattr -r -d com.apple.quarantine 具体应用 # 具体应用是一个路径&#xff0c;拖入 访达——应用程序——第三方应用 到终端就行 # sudo xattr -r -d com.app…

在Widows系统下载安装Ubuntu

1.下载VirtualBox Oracle VM VirtualBox 2.下载安装microsoft visual c 2019 进入百度&#xff1a;百度一下&#xff0c;你就知道 出现下面这个页面时&#xff0c;直接点击修复&#xff0c;点击修复后&#xff0c;出现【重新启动】按钮&#xff0c;点击【重新启动】&#xff…

微信小程序基础加强总结

本篇文章给大家带来了关于微信小程序的相关问题&#xff0c;其中主要介绍了一些基础内容&#xff0c;包括了自定义组件、样式隔离、数据、方法和属性等等内容&#xff0c;下面一起来看一下&#xff0c;希望对大家有帮助。 1、自定义组件 1.1、创建组件 在项目的根目录中&…

电机⽮量控制、直接转矩控制、参数辨识

直接转矩控制是一种控制异步电动机转矩的方式&#xff0c;其基本原理是测量和控制异步电动机定子电流矢量。在直接转矩控制中&#xff0c;定子磁链和转子磁链矢量可以表示为&#xff0c;并通过测量空间电角度的大小&#xff0c;来决定电磁转矩&#xff0c;进而实现直接转矩控制…

Java8实战-总结25

Java8实战-总结25 用流收集数据归约和汇总查找流中的最大值和最小值汇总连接字符串广义的归约汇总 用流收集数据 归约和汇总 重用一下之前的例子&#xff1a;包含一张佳肴列表的菜单。 就像刚刚看到的&#xff0c;在需要将流项目重组成集合时&#xff0c;一般会使用收集器(S…

uniapp 小程序 全局弹窗 每个需要使用的页面都不用再引用

文章目录 创建组件在项目的根目录下的vue.config.vue中配置页面中使用 使用全局组件&#xff0c;先声明全局组件 与普通的组件声明不同之处在于 1&#xff1a;目录形式 2&#xff1a;声明引用方式 创建组件 在components目录中创建组件目录/组件vue&#xff0c;如下 注意需要同…

SpringMVC之综合案例:参数传递,向页面传参,页面跳转

参数传递向页面传参页面跳转 1.参数传递 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"htt…