【C++】搜索二叉树

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、搜索二叉树概念
  • 二、搜索二叉树的操作
    • 1.插入
    • 2. 查找
    • 3. 中序遍历
    • 4. 删除
  • 三、默认成员函数
    • 1.析构函数
    • 2.拷贝构造
    • 3. 赋值运算符重载
  • 四、完整代码


一、搜索二叉树概念

搜索二叉树 也叫做二叉排序树,它要么是一棵空树,要么具有以下性质:
(1)若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
(2)若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
(3)它的左右子树也分别为二叉搜索树

如下就是搜索二叉树,对于任何一个节点,它的左子树的所有节点值都比它小,它的右子树的所有节点值都比它大:

int a[] = {9,6,15,4,13,5,1,20,8,27};

在这里插入图片描述

总结:在左子树值比根小,右子树值比根大。 当树走中序遍历时,序列都是有序的。

搜索二叉树的结构定义:

//定义树的结点
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//构造函数BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};//树结构
template<class K>
class BStree
{typedef BSTreeNode<K> Node;
public://构造函数只需要将根初始化为空就行了BSTree():_root(nullptr){}private:Node* _root;//根
};

提示:以下是本篇文章正文内容,下面案例可供参考

二、搜索二叉树的操作

1.插入

插入节点分两步:

(1)找位置

    ①key比当前节点值大,向右走②key比当前节点值小,向左走③key等于当前节点值,该节点值已经存在,插入失败

(2)插入

    ①key比父亲节点值小就插入父亲左子树②key比父亲节点值大就插入父亲右子树

由于插入后,要将节点链接到树中,因此要定义parent节点,用来链接新节点:
在这里插入图片描述

bool Insert(const K& key){//空树 -》 直接插入if (_root == nullptr){_root = new Node(key);}//寻找插入位置Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}//搜索二叉树中不允许出现相同结点else{return false;}}//找到了插入节点的位置了-》判断插入节点与parent->_key的大小,判断插入到左子树还是右子树if (parent->_key > key){parent->_left = new Node(key);}else{parent->_right = new Node(key);}return true;}//递归插入 形成二叉树
bool _InsertR(Node*& _root, const K& key){//说明找到插入节点的位置了 / 树为NULLif (_root == nullptr){_root = new Node(key);return true;}if (_root->_key > key){return _InsertR(_root->_left, key);}else if (_root->_key < key){return _InsertR(_root->_right, key);}else return false;}

2. 查找

查找比较简单:

    ①key比当前节点值大,向右走②key比当前节点值小,向左走③key等于当前节点值,找到了

在这里插入图片描述

//查找 迭代Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn cur;}return cur;}//查找递归版本Node* _FindR(Node* _root, const K& key){if (_root == nullptr) return _root;if (_root->_key > key) return _FindR(_root->_left, key);else if(_root->_key < key) return _FindR(_root->_right, key);else return _root;}

3. 中序遍历

由于根节点的访问限定符是私有的,那么在main函数中要终须遍历一棵树时,就无法将二叉搜索树的对象根节点传给中序遍历,因为类外面访问不到私有成员。

因此可以这样考虑:这个搜索二叉树对象是有根节点的,可以考虑在里面套一层,给套在里面的函数传参_root ,去递归调用自己:

	//内层函数使用_rootvoid _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->_left);//递归调用自己cout << root->_key << " ";_Inorder(root->_right);//递归调用自己}//先调不传参数的InOrdervoid InOrder(){//把_root传给子函数,让子函数去使用_root_InOrder(_root);cout << endl;}

4. 删除

(1)找位置

    ①key比当前节点值大,向右走②key比当前节点值小,向左走③key等于当前节点值,找到了,准备删除

(2)删除,有两种删除方法:非递归和递归

非递归删除:

①该节点没有孩子,即该节点是叶子节点,删除节点后把父亲指向自己的指针置空

在这里插入图片描述

②该节点有一个孩子,就把该节点的孩子节点的链接给该节点的父亲,顶替自己的位置,①可以当成②的特殊情况
在这里插入图片描述
③该节点有两个孩子,找比它自己的左孩子大,比它自己的右孩子小的节点替换它(也就是拿它的左子树的最大节点或右子树的最小节点替换它),替换之后,该节点就只有一个孩子或没有孩子了,就变成①或②了。
在这里插入图片描述

//删除的非递归版本bool Erase(const K& key){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else  //找到结点 -》 删除结点{// 删除结点的左子树为空 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;}//删除结点的右子树为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_right;}}delete cur;}//删除节点的左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = cur->_right;Node* MinParentNode = cur;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, cur->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}return false;}//递归删除bool _EraseR(Node*&_root, const K& key){if (_root == nullptr)return false;if (key < _root->_key){return _EraseR(_root->_left, key);}else if (_root->_key < key){return _EraseR(_root->_right, key);}else{//删除// 左子树为空 if (_root->_left == nullptr){Node* del = _root;_root = _root->_right;delete del;}//右子树为空else if (_root->_right == nullptr){Node* del = _root;_root = _root->_left;delete del;}//左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = _root->_right;Node* MinParentNode = _root;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, _root->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}

三、默认成员函数

1.析构函数

递归调用子函数去析构

	~BSTree(){Destory(_root);_root = nullptr;}void Destory(Node* _root){if (_root == nullptr) return;Destory(_root->_left);Destory(_root->_right);delete _root;}//中序遍历void _InOrder(Node* _root){if (_root == nullptr) return;_InOrder(_root->_left);cout << _root->_key << " ";_InOrder(_root->_right);}

2.拷贝构造

拷贝构造利用递归调用子函数不断拷贝节点:

	//拷贝构造BSTree(const BSTree<K>& t){_root = t.copy(t._root);}

在子函数处:

	Node* _copy(Node* root){if (root == nullptr)//如果根为空,直接返回{return;}Node* copyNode = new Node(root->_key);//创建根节点copyNode->_left = _copy(root->_left);//递归拷贝左子树节点copyNode->_right = _copy(root->_right);//递归拷贝右子树节点return copyNode;//返回根}

3. 赋值运算符重载

使用节点互换的逻辑

	BSTree<K>& operator=(BSTree<K> t)  //t这里调用的拷贝构造-》深拷贝{swap(_root, t._root);Destory(t._root);t._root = nullptr;return *this;}

四、完整代码

//  .h  文件#pragma once
#include <iostream>
using namespace std;
template <class K>
//定义树的结点
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//构造函数BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};template <class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree() = default;//插入 形成搜索二叉树  迭代版本bool Insert(const K& key){//空树 -》 直接插入if (_root == nullptr){_root = new Node(key);}//寻找插入位置Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}//搜索二叉树中不允许出现相同结点else{return false;}}//找到了插入节点的位置了-》判断插入节点与parent->_key的大小,判断插入到左子树还是右子树if (parent->_key > key){parent->_left = new Node(key);}else{parent->_right = new Node(key);}return true;}//递归插入 形成二叉树bool InsertR(const K& key){return _InsertR(_root, key);}bool Erase(const K& key){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else  //找到结点 -》 删除结点{// 删除结点的左子树为空 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;}//删除结点的右子树为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_right;}}delete cur;}//删除节点的左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = cur->_right;Node* MinParentNode = cur;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, cur->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}return false;}//递归删除bool EraseR(const K& key){return _EraseR(_root, key);}//查找 迭代Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn cur;}return cur;}//查找->递归版本Node* FindR(const K& key){return _FindR(_root, key);}//中序打印void InOrder(){_InOrder(_root);cout << endl;}BSTree(const BSTree<K>& t){_root = Copy(t._root);}~BSTree(){Destory(_root);_root = nullptr;}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);Destory(t._root);t._root = nullptr;return *this;}private://直接给缺省值,调用BSTree默认构造就不用初始化了Node* _root = nullptr;Node* Copy(const Node* _root){if (_root == nullptr) return nullptr;//拷贝根节点  左子树  右子树Node* newnode = new Node(_root->_key);newnode->_left = Copy(_root->_left);newnode->_right = Copy(_root->_right);return newnode;}void Destory(Node* _root){if (_root == nullptr) return;Destory(_root->_left);Destory(_root->_right);delete _root;}//中序遍历void _InOrder(Node* _root){if (_root == nullptr) return;_InOrder(_root->_left);cout << _root->_key << " ";_InOrder(_root->_right);}//递归插入  !!!Node*& _root 就表示当前节点的引用,我直接让_root直接等于new Node(key),直接链接bool _InsertR(Node*& _root, const K& key){//说明找到插入节点的位置了 / 树为NULLif (_root == nullptr){_root = new Node(key);return true;}if (_root->_key > key){return _InsertR(_root->_left, key);}else if (_root->_key < key){return _InsertR(_root->_right, key);}else return false;}//递归删除bool _EraseR(Node*&_root, const K& key){if (_root == nullptr)return false;if (key < _root->_key){return _EraseR(_root->_left, key);}else if (_root->_key < key){return _EraseR(_root->_right, key);}else{//删除// 左子树为空 if (_root->_left == nullptr){Node* del = _root;_root = _root->_right;delete del;}//右子树为空else if (_root->_right == nullptr){Node* del = _root;_root = _root->_left;delete del;}//左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = _root->_right;Node* MinParentNode = _root;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, _root->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}//查找递归版本Node* _FindR(Node* _root, const K& key){if (_root == nullptr) return _root;if (_root->_key > key) return _FindR(_root->_left, key);else if(_root->_key < key) return _FindR(_root->_right, key);else return _root;}
};//  .cpp 文件#include"BinarySearchTree.h"int main()
{BSTree<int> root;int Tree[] = { 7,2,4,6,3,1,5};for (int i = 0; i < (sizeof(Tree) / sizeof(Tree[0])); i++){root.InsertR(Tree[i]);}root.InOrder();BSTree<int> root1 = root;root1.InOrder();}

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

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

相关文章

SSM宾馆客房管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 宾馆客房管理系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统…

任正非说:扩张必须踩在坚实的基础上,擅自扩张只能是自杀。

嗨&#xff0c;你好&#xff01;这是华研荟【任正非说】系列的第23篇文章&#xff0c;让我们继续聆听任正非先生的真知灼见&#xff0c;学习华为的管理思想和管理理念。 一、要想赢&#xff0c;要么在剑法上高于人&#xff0c;要么在盾牌上坚于人。若果剑不如人&#xff0c;就要…

大厂面试题-JVM为什么使用元空间替换了永久代?

目录 面试解析 问题答案 面试解析 我们都知道Java8以及以后的版本中&#xff0c;JVM运行时数据区的结构都在慢慢调整和优化。但实际上这些变化&#xff0c;对于业务开发的小伙伴来说&#xff0c;没有任何影响。 因此我可以说&#xff0c;99%的人都回答不出这个问题。 但是…

一文读懂:到底什么是Chiplets?

50多年来&#xff0c;Intel和AMD一直是两家主流的处理器公司。 虽然两者都使用x86 ISA来设计新品&#xff0c;但是在过去十多年左右的时间里&#xff0c;两家cpu公司却走上了完全不同的道路。 大约在2000年代中期&#xff0c;随着推土机(Bulldozer)芯片推出&#xff0c;AMD在…

Flask基本教程以及Jinjia2模板引擎简介

flask基本使用 直接看代码吧&#xff0c;非常容易上手&#xff1a; # 创建flask应用 app Flask(__name__)# 路由 app.route("/index", methods[GET]) def index():return "FLASK&#xff1a;欢迎访问主页&#xff01;"if __name__ "__main__"…

OpenCV学习笔记

OpenCV基础 threshold函数的使用 https://blog.csdn.net/u012566751/article/details/77046445 图像的二值化就是将图像上的像素点的灰度值设置为0或255&#xff0c;这样将使整个图像呈现出明显的黑白效果。在数字图像处理中&#xff0c;二值图像占有非常重要的地位&#xff0…

线扫相机DALSA--常见问题一:软件安装顺序

1.软件安装顺序 先安装&#xff1a;Sapera_LT_SDK&#xff0c;后安装Xtium-CL MX4驱动。 2.初次安装CamExpert&#xff0c;重启电脑后未找到相机 Settings(搜索协议)配置完毕后&#xff0c;需点击Detect Camera(一键查找相机)按钮&#xff0c;搜索相机。第一次查找相机耗时会略…

聚观早报 |蔚来推出婚车服务;长城汽车第三季度财报

【聚观365】10月30日消息 蔚来推出婚车服务 长城汽车第三季度财报 AI汽车机器人极越01上市 谷歌投资初创公司Anthropic 东方财富第三季度营收 蔚来推出婚车服务 据蔚来汽车官方消息&#xff0c;蔚来宣布推出“蔚来用户专享”的婚庆用车定制服务。 据悉&#xff0c;该服务…

Mac删除照片快捷键ctrl加什么 Mac电脑如何批量删除照片

Mac电脑是很多人喜欢使用的电脑&#xff0c;它有着优美的设计、高效的性能和丰富的功能。如果你的Mac电脑上存储了很多不需要的照片&#xff0c;那么你可能会想要删除它们&#xff0c;以节省空间和提高速度。那么&#xff0c;Mac删除照片快捷键ctrl加什么呢&#xff1f;Mac电脑…

微信小程序实现微信登录(Java后台)

这两天在自己的小项目中加入了微信小程序的很多功能&#xff0c;今天来说一下关于微信授权登录的部分。 需要的材料 1&#xff1a;一个可以测试的微信小程序 2&#xff1a;此微信小程序的APPID和APPscret 流程 微信用户对应一个小程序都有一个唯一的openid&#xff0c;微信…

【文末送书】AI时代数据的重要性

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

分享个自己写的小程序解包工具

闲聊 前几天在吾爱破解上看到一个小程序逆向的帖子&#xff1a;windows下通杀wx小程序云函数实战 &#xff0c;想着自己也学习一下怎么逆向小程序&#xff0c;一搜 unveilr 仓库没了&#xff0c;看评论好像开始收费了。 我就用aardio写了一个解密和解包工具&#xff0c;这里免…

Kotlin协程核心理解

一、协程是什么&#xff1f; 1.1 基本概念的理解 我们知道JVM中的线程的实现是依赖其运行的操作系统决定的&#xff0c;JVM只是在上层进行了API的封装&#xff0c;包含常见的有线程的启动方法&#xff0c;状态的管理&#xff0c;比如&#xff1a;Java中抽象出了6种状态&#x…

深入理解指针3

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 字符指针变量 2. 数组指针变量 2.1 数组指针变量是什么&#xff1f; 2.2 数组指针变量怎么初始化 3. 二维数组传参的本质 4. 函数指针变量 4.1 函数指针变量的创…

并发安全问题之超卖问题

并发安全问题之超卖问题 乐观锁总结&#xff1a; 优点&#xff1a;不加锁性能好。 缺点&#xff1a;同时请求成功率低&#xff08;即只要发现数据变了就放弃了&#xff09;。 乐观锁思想的具体体现&#xff1a;一共两步&#xff0c;第一步&#xff0c;先查询状态。第二步&…

【数据库】

文章目录 1. 聚合函数练习&#xff1a; 2. 子查询 1. 聚合函数 where中过滤条件中不能写聚合函数&#xff0c;有聚合函数需要写到Having中 方式一效率高&#xff1a; Select执行流程 练习&#xff1a; 2. 第七题&#xff1a;count(*)有问题&#xff0c;原因是左外连接后…

Apache Doris (四十八): Doris表结构变更-替换表

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录

Leetcode—275.H指数II【中等】

2023每日刷题&#xff08;十三&#xff09; Leetcode—275.H指数II 算法思想 实现代码 int minValue(int a, int b) {return a < b ? a : b; }int hIndex(int* citations, int citationsSize){int left, right;left 0;right citationsSize - 1;while(left < right) …

Openssl数据安全传输平台017:Linux客户端代码的编译与调试-Bug记录

文章目录 1 在windows上先预编译2 Centos上进入项目文件夹进行编译2.0 最终的编译指令2.1 找不到lprotobuf&#xff0c;找不到protobuf的google文件夹2.1.1 编译指令及提示2.1.2 问题分析2.1.3 解决办法 2.2 json类中方法unreference2.2.1 编译指令及提示2.2.2 问题分析 *** 最…

【工具使用】NPS内网穿透工具介绍

文章目录 前言一、内网穿透二、NPS概述三、NPS原理四、NPS服务器搭建(一)云服务器配置 五、NPS内网穿透演示(一)演示案例一(二)演示案例二 六、NPS内网穿透检测建议(一)流量监控(二)流量协议分析(三)网络行为异常检测 七、NPS内网穿透防范建议(一)阻止或隔离流量(二)更新和强化…