C++泛型实现搜索二叉树

文章目录

    • 二叉搜索树
      • 查找
      • 插入
      • 删除
      • 实现
      • 应用
      • 性能分析

二叉搜索树

二叉搜索树(BST,Binary Search Tree)又称为二叉排序树,空树也算

二叉搜索树有如下性质

  • 若左子树不为空,则左子树上所有节点值小于根节点
  • 若右子树不为空,则右子树上所有节点值大于根节点
  • 左子树和右子树也都是二叉搜索树

例如

屏幕截图 2024-03-08 204448.png

当然如果左大右小也可以

二叉搜索树的一个性质是中序遍历有序

查找

从根节点开始查找比较,比根大向右查找,比根小向左查找

最多查找高度次,如果没找到就代表值不存在

插入

如果为空,新增节点

如果不为空,按照性质插入节点

删除

首先需要确定值是否在二叉树中

要删除就右四种情况

  1. 无子节点——直接删除即可,可以合并到只有一个节点的情况
  2. 只有左节点——删除,令该节点的父节点指向左节点
  3. 只有右节点——删除,令该节点的父节点指向右节点
  4. 有两个子节点——在左子树寻找关键之最大的节点或右子树的最小节点,以最小节点为例,找到最小节点后与删除节点替换,再处理替换后的节点删除问题

实现

#pragma once
#include<iostream>using namespace std;template<class K> 
struct BSTreeNode // 二叉树节点,K表示关键字
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(cosnt K& key):_left(nullptr),_right(nullptr),_key(key){}
};template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:// C++11BSTree() = default; // 强制生成默认构造~BSTree(){Destroy(_root);}BSTree(const BSTreeNode<K>& t){_root = Copy(t._root);}BSTree<K>& operator=(BSTree<k> t){swap(_root, t._root);return *this;}bool Insert(const K& key) // 建树,插入{if (_root == nullptr) // 空树{_root = new Node(key);return tree;}Node* parent = nullptr;Node* cur = _root;while (cur) // 找位置{parent = cur;if (cur->_key < key)cur = cur->_left; // 插入值比当前值小,进左树else if (cur->_key > key)cur = cur->_right; // 插入值比当前值大,进右树elsereturn false; // 不允许出现重复值}cur = new Node(key);if (parent->_key < key) // 连接父节点parent->_right = cur;elseparent->_left = cur;}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;elsereturn true;  }return false;}bool Erase(const K& 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 // 找到了,准备删除{if (cur->_left == nullptr) // 左子树为空{// 当删除节点为根节点时,直接让根节点变为右子树即可if (cur == _root){_root = cur->_right;}// 当删除节点不是根节点时,需要连接父节点和右子树else{// 判断当前节点是父节点的左孩子还是右孩子,确保正确连接if (cur == parent->_left)// 这里为什么不用防止parent是空指针呢// 因为只有根节点没有父节点,而前面一个判断已经把根节点单独处理过了parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;}else if (cur->_right == nullptr) // 右子树为空,与左子树为空类似{if (cur == _root){_root = cur->_left;}else{if (cur == parent->_left) parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;}else // 左右都不为空{// 找到右子树的最小节点,替换后删除parent = cur; // 因为后面需要替换,防止出现解引用空指针Node* SubLeft = cur->_right; // 表示右子树最小值,他一定在右子树的最高的最左边的那个节点上while (SubLeft->_left) // 找到该节点{parent = SubLeft;SubLeft = SubLeft->_left;}swap(cur->_key, SubLeft->_key); // 交换节点值// 最左节点一定是左子树为空,因此只需要父节点连接最左节点的右子树if (SubLeft == parent->_left) // 判断该节点是父节点的左节点还是右节点,再连接parent->_left = SubLeft->_right;elseparent->_right = SubLeft->_right;delete SubLeft;}return true;}return false;}}void InOrder() // 中序遍历打印{// 中序遍历需要根节点,又不希望类外得到根节点// 这里可以只实现一个接口,内容是private即可// 后面的同理_InOrder(_root);cout << endl;}bool FindR(const K& key) // 递归查找{return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, K);}bool EraseR(const K& key){return _EraseR(_root, key);}
private: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;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ' ';_InOrder(root->_right);}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);elsereturn true;}bool _InsertR(Node*& root, const K& key){if (root == nullptr){// 这里的根节点是父节点左子树或者右子树的引用// 因此直接赋值就能连接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);elsereturn 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{if (root->_left == nullptr){Node* del = root;root = root->_right; // root也是父节点左右子树的别名,因此直接赋值delete del;return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete del;return true;}else{Node* SubLeft = root->_right;while (SubLeft->_left)SubLeft = SubLeft->_left;swap(root->_key, SubLeft->_key);// 替换之后,转换成在右子树递归删除即可return _EraseR(root->_right, key);}}}Node* _root = nullptr;
};

应用

二叉搜索树一般有两个应用

第一类是K模型,结构中只需要存储Key即可,关键之就是所需要的值,一般用于检测某个值是否存在

第二类是KV模型,结构中是<Key,Value>键值对,类似于字典

性能分析

插入和删除都必须先查找

插入的次序不同,会影响到二叉树的结构

最优情况下,二叉树为完全二叉树,其平均比较次数为 log ⁡ 2 N \log_2N log2N

最差情况下,二叉树为单支树,其平均比较次数为 N 2 \frac{N}{2} 2N

因此当二叉树为单支树,我们应当如何改进,使其性能都达到最优,就需要引入AVL树和红黑树,这些我们在后面也会陆续讲解和实现

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

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

相关文章

2575. 找出字符串的可整除数组(Go语言)

https://leetcode.cn/problems/find-the-divisibility-array-of-a-string/ 在看题解之前&#xff0c;我的代码是以下这样&#xff1a; package mainimport ("fmt" )func main() {fmt.Println(divisibilityArray("998244353", 3)) }func divisibilityArray…

供应链管理系统(SCM):得供应链得天下不是空话。

2023-08-26 15:51贝格前端工场 Hi&#xff0c;我是贝格前端工场&#xff0c;优化升级各类管理系统的界面和体验&#xff0c;是我们核心业务之一&#xff0c;欢迎老铁们评论点赞互动&#xff0c;有需求可以私信我们 一、供应链对于企业的重要性 供应链对企业经营的重要性不可…

使用plasmo框架开发浏览器插件,注入contents脚本和给页面添加UI组件

plasmo&#xff1a;GitHub - PlasmoHQ/plasmo: &#x1f9e9; The Browser Extension Framework plasmo是一个开发浏览器插件的框架&#xff0c;支持使用react和vue等技术&#xff0c;而且不用手动管理manifest.json文件&#xff0c;框架会根据你在框架中的使用&#xff0c;自…

ChatGPT高效提问——角色提示

ChatGPT高效提问——角色提示 角色提示技巧是一种通过给模型提供具体的角色扮演&#xff0c;指导ChatGPT输出的方法。这个技巧对一个具体的上下文或者听众定制生成的文本很有用。 要使用角色提示技巧&#xff0c;你需要提供明确具体的模型扮演的角色。 例如&#xff0c;如果…

如何在Windows环境下编译OpenOCD

1. 安装Cygwin Windows环境下编译OpenOCD可以是在MinGW-w64/MSYS或Cygwin下&#xff0c;这里选择Cygwin&#xff0c;下载安装Cygwin。 2. 进入OpenOCD源代码目录 打开Cygwin&#xff0c;进入OpenOCD源代码目录&#xff0c;例如代码放在D:\Temp\OpenOCD\openocd-code下&#…

C++学习笔记:AVL树

AVL树 什么是AVL树?AVL树节点的定义AVL树的插入平衡因子调整旋转调整左旋转右旋转左右双旋右左双旋 AVL树完整代码实现 什么是AVL树? AVL是1962年,两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis 为了解决如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找…

限制员工上网行为,如何有效管控员工上网行为? 你一定想不到这个方法!

发现员工上班时间刷抖音&#xff1a; 面对这种情况&#xff0c;领导不得火冒三丈&#xff1f;&#xff1f;&#xff1f; 对于员工不恰当的上网行为&#xff0c;非常有可能导致工作效率低下、安全风险增加以及企业形象受损。 因此应该采取一些措施来对员工上网行为进行管理。 …

第三节:在Sashulin中自定义组件

上一节讲解了如何建立一个业务消息流&#xff0c;流程是由组件构成的。目前SMS提供了General、Database、MessageQueue、Socket、WebService、Http、Internet等系列常用组件&#xff0c;如果不满足业务需求&#xff0c;可以进行自定义组件开发。 一、组件开发 1、建立一个Jar…

C及C++每日练习(3)

选择题&#xff1a; 1.以下程序的输出结果是&#xff08;&#xff09; #include <stdio.h> main() { char a[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, *p; int i; i 8; p a i; printf("%s\n", p - 3); } A.6 B. 6789 C. 6 D.789 对于本题&#xff0…

吴恩达机器学习-可选实验室:特征工程和多项式回归(Feature Engineering and Polynomial Regression)

文章目录 目标工具特征工程和多项式回归概述多项式特征选择功能备用视图扩展功能复杂的功能 恭喜! 目标 在本实验中&#xff0c;你将:探索特征工程和多项式回归&#xff0c;它们允许您使用线性回归的机制来拟合非常复杂&#xff0c;甚至非常非线性的函数。 工具 您将利用在以…

2023最新pytorch安装教程,简单易懂,面向初学者(Anaconda+GPU)

一、前言 目前是2023.1.27,鉴于本人安装过程中踩得坑&#xff0c;安装之前我先给即将安装pytorch的各位提个醒&#xff0c;有以下几点需要注意 1.判断自己电脑是否有GPU 注意这点很重要&#xff0c;本教程面向有NVIDA显卡的电脑&#xff0c;如果你的电脑没有GPU或者使用AMD显…

STM32day3

1.思维导图 1.总结任务的调度算法&#xff0c;把实现代码再写一下 /* Definitions for myTask02 */ osThreadId_t myTask02Handle; uint32_t myTask02Buffer[ 64 ]; osStaticThreadDef_t myTask02ControlBlock; const osThreadAttr_t myTask02_attributes {.name "myTa…

代码随想录算法训练营第三十九天|62.不同路径、63. 不同路径 II

62.不同路径 刷题https://leetcode.cn/problems/unique-paths/description/文章讲解https://programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html视频讲解https://www.bilibili.com/video/BV1ve4y1x7Eu/?vd_sourceaf4853e80f89e28094a5fe1e220d9062 题解&…

react的diff源码

react 的 render 阶段&#xff0c;其中 begin 时会调用 reconcileChildren 函数&#xff0c; reconcileChildren 中做的事情就是 react 知名的 diff 过程 diff 算法介绍 react 的每次更新&#xff0c;都会将新的 ReactElement 内容与旧的 fiber 树作对比&#xff0c;比较出它们…

md5绕过

文章目录 \\和\\\md5数组绕过科学计数法绕过双md加密md5碰撞Hash长度攻击 下面会以同一道题给大家演示&#xff1a; (题目来源与nssctf) 和 在php代码中我们会看到和&#xff0c;虽然两个都是表示相等&#xff0c;但是在细节上会有所部区别 &#xff1a;是弱比较&#xff0c;只…

0201安装报错-hbase-大数据学习

1 基础环境简介 linux系统&#xff1a;centos&#xff0c;前置安装&#xff1a;jdk、hadoop、zookeeper&#xff0c;版本如下 软件版本描述centos7linux系统发行版jdk1.8java开发工具集hadoop2.10.0大数据生态基础组件zookeeper3.5.7分布式应用程序协调服务hbase2.4.11分布式…

Sora 作者被曝读博期间仅发表两篇论文,我们是否需要重塑科研价值观?

众所周知&#xff0c;在当今学术界&#xff0c;论文数量和产出速度常常被视为研究者生产力和学术成就的重要标尺。笔者也面试过很多博士生候选人&#xff0c;大家普遍会以自己读博期间发表过10几篇甚至几十篇论文而骄傲&#xff0c;很少有候选人会强调自己读博期间虽然发表论文…

Matlab|基于目标级联法的微网群多主体分布式优化调度

目录 主要内容 1.1 上层微网群模型 1.2 下层微网模型 部分程序 实现效果 下载链接 主要内容 本文复现《基于目标级联法的微网群多主体分布式优化调度》文献的目标级联部分&#xff0c; 建立微网群系统的两级递阶优化调度模型: 上层是微网群能量调度中心优化调度…

java中的字符串比较(题目作示例)

错误的代码 import java.util.Scanner; public class one {public static void main(String[] args) {Scanner scnew Scanner(System.in);String b"47568";int i0;for ( i 0; i <3; i){String asc.next();if(ab){System.out.println("密码正确&#xff0c;登…

在外包公司干了4年,技术退步2年...

先说情况&#xff0c;大专毕业&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近6年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…