C++的二叉搜索树

news2025/1/12 15:52:56

目录

基本概念

二叉搜索树的实现 

插入结点

查找结点

删除结点

删除结点左为空

删除结点右为空

基于特殊情况的优化

删除结点左右不为空

基于特殊情况的优化

完整代码 

二叉搜索树的实际应用

K和KV模型

改造二叉搜索树为为KV模型


基本概念

1、二叉搜索树又称二叉排序树

2、左子树上的所有值均小于根节点,右子树上的所有值均大于根节点

3、一个结点的左右子树均为二叉搜索树

二叉搜索树的实现 

插入结点

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;//成功插入返回true
}

查找结点

bool 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 true;//遍历找到了cur->key== key就返回true
		}
	}
	return false;//没找到就返回false
}

删除结点

删除结点左为空

//删除结点的左节点为空
if (cur->left == nullptr)
{
	if(cur == parent->left)
	{
		parent->left = cur->right;
	}
	else
	{
		parent->right = cur->left;
	}
	delete cur;
}

删除结点右为空

//删除结点的右节点为空
else if (cur->right == nullptr)
{
	if (cur == parent->left)
	{
		parent->left = cur->left;
	}
	else
	{
		parent->right = cur->left;
	}
	delete cur;
}

基于特殊情况的优化

//删除结点的左节点为空
if (cur->left == nullptr)
{
	//删除结点是根节点
	if (cur == root)
	{
		root = cur->right;
	}
	else
	{
		if(cur == parent->left)
		{
			parent->left = cur->right;
		}
		else
		{
			parent->right = cur->left;
		}
	}
	delete cur;
}

//删除结点的右节点为空
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;
}

删除结点左右不为空

图画反了 

//删除结点的左右结点均不为空
else
{
	//查找右子树的最左结点替代删除
	Node* rightMinParent = null;//记录交换结点的父亲结点
	Node* rightMin = cur->right;//记录交换节点

	//遍历寻找删除结点的右子树的最左结点
	while (rightMin->left)
	{
		rightMinParent = rightMin;
		rightMin = rightMin->left;
	}
				
	swap(cur->key, rightMin->key);
	rightMinParent->left = rightMin->right;//防止交换结点点还有右子树(交换结点不可能有左子树,
    //因为交换结点就是删除结点的右子树的最左结点,如果它还有左子树那么最左结点就不是它)
	delete rightMin;//rightMin负责找到交换结点,找到并交换后它没用了可以直接删除
}

基于特殊情况的优化

//删除结点的左右结点均不为空
else
{
	//查找右子树的最左结点替代删除
	Node* rightMinParent = cur;//如果要删除的是根节点,(即使不删除根节点,一旦进入循环则parent也会直接发生变化)
	Node* rightMin = cur->right;//记录交换节点
	//遍历寻找
	while (rightMin->left)
	{
		rightMinParent = rightMin;
		rightMin = rightMin->left;
	}
				
	swap(cur->value, rightMin->key);
    //rightMin是parent的左,就令parent的左指向rightMin的右
	if (rightMinParent->left == rightMin)//不删除根节点时都会满足该条件
		rightMinParent->left = rightMin->right;
    //rightMin是parent的右,就令parent的左指向rightMin的右
	else
		rightMinParent->right = rightMin->right;//处理删除根节点的特殊情况
	delete rightMin;
}

完整代码 

#pragma once
#include <iostream>
using namespace std;

template<class T>
struct  BSTreeNode
{
	BSTreeNode<T>* left;
	BSTreeNode<T>* right;
	T value;

	BSTreeNode(const T& key)
		:left(nullptr)
		,right(nullptr)
		,value(key)
	{}
};


template<class T>
class  BSTree
{
	typedef BSTreeNode<T> Node;
public:
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;//成功插入返回true
}

bool 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 true;
		}
	}
	return false;
}

bool erase(const T& key)
{
	Node* parent = nullptr;
	Node* cur = root;
	while (cur)
	{
		if (cur->key< key)
		{
			cur = cur->right;
		}
		else if (cur->key> key)
		{
			cur = cur->left;
		}
		//遍历寻找到要删除的值
		else
		{
			//删除结点的左节点为空
			if (cur->left == nullptr)
			{
				//删除结点是根节点
				if (cur == root)
				{
					root = cur->right;
				}
				else
				{
					if(cur == parent->left)
					{
					parent->left = cur->right;
					}
					else
					{
						parent->right = cur->left;
					}
				}
				delete cur;
			}

			//删除结点的右节点为空
			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;
			}
			//删除结点的左右结点均不为空
			else
			{
				//查找右子树的最左结点替代删除
				Node* rightMinParent = cur;
				Node* rightMin = cur->right;
				//遍历寻找
				while (rightMin->left)
				{
					rightMinParent = rightMin;
					rightMin = rightMin->left;
				}
				
				swap(cur->key, rightMin->key);
				if (rightMinParent->left == rightMin)
					rightMinParent->left = rightMin->right;
				else
					rightMinParent->right = rightMin->right;
				delete rightMin;
			}
			return true;
		}
	}
	return false;
}

public:
	//套一层(友元、套一层、get三种方式获取类内的数据)
	void InOrder()
	{
		_InOrder(root);
		cout << endl;
	}

private:

	//循环遍历
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		} 

		_InOrder(root->left);
		cout << root->key<< " ";
		_InOrder(root->right);
	}

private:
	Node* root = nullptr;
};

void test()
{
	int a[] = { 8,3,1,10,6,4,7,14,13 };
	BSTree<int> t1;
	//循环插入
	for (auto e : a)
	{
		t1.Insert(e);
	}

	//中序遍历
	t1.InOrder();

	//删除结点
	t1.erase(8);

	//中序遍历
	t1.InOrder();
}

  • 时间复杂度:O(n)或 O(logn)
  • O(n)

  • O(logn)

二叉搜索树的实际应用

K和KV模型

K模型:只有key作为关键码,结构中只需存储key即可,key就是要搜索的值(以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误)

KV模型:每一个关键码key都有与之对应的值value,即<key,value>键值对(英汉词典中的中英文之间的对应关系<word,chinese>,通过中文可以快速找到对应的中文,统计单词的出现次数,统计成功后,给定某一个单词就能快速找到其出现的次数<word,count>)

查找的方式:

  • 二分查找
  • 二叉树搜索查找 -> AVL树和红黑树
  • 哈希查找
  • 跳表查找
  • 多叉搜索树查找:B树系列

改造二叉搜索树为为KV模型

//KV模型
namespace key_value
{
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;//成功插入返回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 cur;
}

bool erase(const K& key)
{
	Node* parent = nullptr;
	Node* cur = root;
	while (cur)
	{
		if (cur->key < key)
		{
			cur = cur->right;
		}
		else if (cur->key > key)
		{
			cur = cur->left;
		}
		//遍历寻找到要删除的值
		else
		{
			//删除结点的左节点为空
			if (cur->left == nullptr)
			{
				//删除结点是根节点
				if (cur == root)
				{
					root = cur->right;
				}
				else
				{
					if (cur == parent->left)
					{
						parent->left = cur->right;
					}
					else
					{
						parent->right = cur->left;
					}
				}
				delete cur;
			}

			//删除结点的右节点为空
			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;
			}
			//删除结点的左右结点均不为空
			else
			{
				//查找右子树的最左结点替代删除
				Node* rightMinParent = cur;
				Node* rightMin = cur->right;
				//遍历寻找
				while (rightMin->left)
				{
					rightMinParent = rightMin;
					rightMin = rightMin->left;
				}

				swap(cur->key, rightMin->key);
				if (rightMinParent->left == rightMin)
					rightMinParent->left = rightMin->right;
				else
					rightMinParent->right = rightMin->right;
				delete rightMin;
			}
			return true;
		}
	}
	return false;
}

public:
	//套一层(友元、套一层、get三种方式获取类内的数据)
	void InOrder()
	{
		_InOrder(root);
		cout << endl;
	}

private:

	//循环遍历
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->left);
		cout << root->key << ":" <<root->_value<<endl;
		_InOrder(root->right);
	}

private:
	Node* root = nullptr;
};

	void test()
	{
		BSTree<string, string> dict;
		dict.Insert("string","字符串");
		dict.Insert("left", "左边");
		dict.Insert("insert", "插入");

		string str;
		while (cin >> str)
		{
			BSTreeNode<string, string>* ret = dict.Find(str);//Find函数的返回值变为了结点的指针
			if (ret)
			{
				cout << ret->_value << endl;
			}
			else
			{
				cout << "无此单词,请重新输入" << endl;
			}
		}
	}

	void test1()
	{
		//统计次数
		string arr[] = { "苹果","西瓜","香蕉","西瓜","香蕉" ,"西瓜","香蕉" ,"西瓜","草莓" };
		BSTree<string, int> countTree;
		for (const auto& str : arr)
		{
			auto ret = countTree.Find(str);
			if (ret == nullptr)
			{
				countTree.Insert(str, 1);
			}
			else
			{
				ret->_value++;
			}
		}
		countTree.InOrder();
	
	}
}

~over~

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

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

相关文章

数据结构之二叉搜索树底层实现洞若观火!

目录 题外话 正题 二叉搜索树 底层实现 二叉搜索树查找操作 查找操作思路 查找代码实现详解 二叉搜索树插入操作 插入操作思路 插入代码详解 二叉搜索树删除操作 删除操作思路 删除代码详解 小结 题外话 我的一切都是党给的,都是人民给的,都是家人们给的!! 十分感…

LeetCode:51. N 皇后

leetCode51.N皇后 题解分析 代码 class Solution { public:int n;vector<vector<string>> ans;vector<string> path;vector<bool> col, dg,udg;vector<vector<string>> solveNQueens(int _n) {n _n;col vector<bool> (n);dg …

通过matlab对比遗传算法优化前后染色体的变化情况

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 通过matlab对比遗传算法优化前后染色体的变化情况. 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 3.核心程序 ....................................…

AI视频教程下载:用ChatGPT和 MERN 堆栈构建 SAAS 项目

这是一个关于 掌握ChatGPT 开发应用的全面课程&#xff0c;它将带领你进入 AI 驱动的 SAAS 项目的沉浸式世界。该课程旨在使你具备使用动态的 MERN 堆栈和无缝的 Stripe 集成来构建强大的 SAAS 平台所需的技能。 你将探索打造智能解决方案的艺术&#xff0c;深入研究 ChatGPT 的…

第十五章数据管理成熟度评估6分

15.1 引言 能力成熟度评估&#xff08;Capability Maturity Assessment&#xff0c;CMA&#xff09; 是一种基于能力成熟度模型&#xff08;Capability MaturityModel&#xff0c;CMM&#xff09;框架的能力提升方案&#xff0c;描述了数据管理能力初始状态发展到最优化的过程…

一键部署,隐私无忧!有道QAnything,本地AI问答系统开源了,你下载了吗?

作者&#xff1a;Aitrainee | 公众号&#xff1a;AI进修生 排版太难了&#xff0c;请点击这里查看原文&#xff1a;一键部署&#xff0c;隐私无忧&#xff01;有道QAnything&#xff0c;本地AI问答系统开源了&#xff0c;你下载了吗&#xff1f; 一键部署&#xff0c;隐私无忧…

AWVS的使用

AWVS的使用 1、使用docker拉取AWVS的镜像 docker pull secfa/docker-awvs 2.使用AWVS docker run -it -d -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs 3.访问 4.输入账号密码 https://hub.docker.com/r/secfa/docker-awvs 找到账号密码 username:adminadmin.…

azure云服务器学生认证优惠100刀续订永久必过方法记录

前面的话 前几天在隔壁网站搞了个美国edu邮箱&#xff0c;可以自定义用户名。今天就直接认证Azure&#xff0c;本来打算等GitHub学生包过期后用这个edu邮箱重新认证白嫖Azure的。在昨天无意中看到续期&#xff0c;就把原本那个Azure账号续了一年&#xff0c;所以这个美国edu邮…

设计模式六大原则详解

引言 对于设计模式&#xff0c;自己很早之前就看了好多本设计模式书籍&#xff0c;其中一些还看了好几遍&#xff0c;也一直希望自己能在编码的时候把这些设计模式用上去。可是&#xff0c;在日常的打码中&#xff0c;用的做多的就是单例&#xff0c;其次是观察者和建造者模式…

《QT实用小工具·四十一》无边框窗口

1、概述 源码放在文章末尾 该项目实现了无边框窗口效果&#xff0c;项目demo如下所示&#xff1a; 项目代码如下所示&#xff1a; #include "framelesswindow.h" #include <QGuiApplication> #include <QScreen>#ifdef Q_OS_WIN #include <window…

【初阶数据结构】——循环队列

文章目录 1. 什么是循环队列&#xff1f;2. 结构的选择&#xff1a;数组 or 链表&#xff1f;链表结构分析数组结构分析判空判满入数据出数据取队头队尾元素 3. 代码实现&#xff08;数组结构&#xff09;C语言版本C版本 这篇文章我们来学习一下如何实现循环队列 那力扣上呢有一…

VBA隐藏技术stomping

1.简介 之前我们介绍了VBA脚本文件的重定向&#xff0c;修改文件中的加载结构并将脚本的二进制文件进行伪装&#xff0c;达到宏代码隐藏的目的&#xff0c;细节请参考上一篇文章"VBA脚本重定向"。该技术具有一定的局限性&#xff0c;只使用脚本重定向技术无法绕过Mic…

http是什么?http的基础知识教程详解(2024-04-24)

1、http的概念 HTTP&#xff08;超文本传输协议&#xff0c;HyperText Transfer Protocol&#xff09;是一种用于分布式、协作式、超媒体信息系统的应用层协议。 HTTP 是万维网&#xff08;WWW&#xff09;的数据通信的基础&#xff0c;设计目的是确保客户端与服务器之间的通…

乐鑫科技收购创新硬件公司 M5Stack 控股权

乐鑫科技 (688018.SH) 宣布收购 M5Stack&#xff08;明栈信息科技&#xff09;的控股权。这一战略举措对于物联网和嵌入式系统领域的两家公司来说都是一个重要的里程碑&#xff0c;也契合了乐鑫和 M5Stack 共同推动 AIoT 技术民主化的愿景。 M5Stack 以其创新的硬件开发方式而闻…

Linux实现文件共享

#nfs-utils、rpcbind 软件包来提供 NFS 共享服务 #客户端创建共享文件夹&#xff1a; nmcli c reload nmcli c up ens160 systemctl stop firewalld systemctl disable firewalld rpm -q nfs-utils rpcbind #查看是否安装 systemctl enable rpcbind systemctl enable nfs…

(八)小案例银行家应用程序-排序-数组排序

排序一直有很多的算法&#xff0c;今天我们仅仅来说JavaScript内置的排序方法 ● 字符串 const owners [Jonas, Zach, Adam, Martha]; console.log(owners.sort()); console.log(owners);但是注意&#xff0c;这个方法会改变原有的数组&#xff1b; ● 我们在试试数字 cons…

港股“AIGC第一股”出门问问,凭借什么产品做到上市?

随着人工智能技术的飞速发展&#xff0c;AIGC&#xff08;人工智能生成内容&#xff09;领域逐渐成为资本市场的新宠。在这样的背景下&#xff0c;出门问问&#xff08;股票代码&#xff1a;2438.HK&#xff09;作为AIGC领域的先行者&#xff0c;于2024年4月24日正式登陆港交所…

服务器数据恢复—StorNext文件系统下raid5阵列数据恢复案例

服务器数据恢复环境&#xff1a; 昆腾某型号存储&#xff0c;8个存放数据的存储柜1个存放元数据的存储柜。 元数据存储&#xff1a;8组RAID1阵列1组RAID10阵列4个全局热备硬盘。 数据存储&#xff1a;32组RAID5阵列&#xff0c;划分2个存储系统。 服务器故障&#xff1a; 数据…

matplotlib从起点出发(15)_Tutorial_15_blitting

0 位图传输技术与快速渲染 Blitting&#xff0c;即位图传输、块传输技术是栅格图形化中的标准技术。在Matplotlib的上下文中&#xff0c;该技术可用于&#xff08;大幅度&#xff09;提高交互式图形的性能。例如&#xff0c;动画和小部件模块在内部使用位图传输。在这里&#…

Element-plus DatePicker 日期选择器【正则校验时间范围】

效果图&#xff1a; 利用element-plus中的form表单验证完成效果。 <el-form-item label"检查计划截止日期&#xff1a;" prop"deadline"><el-date-pickerv-model"form.deadline"value-format"YYYY-MM-DD"style"width: …