【C++】:搜索二叉树

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关多态的知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

​ 

目录

1. 搜索二叉树

1.1 概念

1.2 搜索二叉树操作

2. 模拟实现搜索二叉树 

2.1 非递归版本

2.1.1 基本构造

2.1.2 插入

2.1.3 删除

2.1.4 查找

2.2 递归版本

2.2.1 插入

2.2.2 删除

2.2.3 查找

2.2.4 中序遍历

3. 完整代码


1. 搜索二叉树

1.1 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

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

1.2 搜索二叉树操作

 

1. 二叉树的查找

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到空,还没找到,这个值不存在。

2. 二叉树的插入

插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

3. 二叉树的删除 

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点

2. 模拟实现搜索二叉树 

搜索二叉树有两种模型:

1. Key模型:节点中只存在一个值key,并且这个值不可以修改,比如后面学习到的set

2. Key_Value模型:节点中存在两个值,一个是key,不可修改,另一个是与key对应的value,可以修改,比如后面学习到的map

在这里我们只实现Key模型的搜索二叉树

2.1 非递归版本

2.1.1 基本构造
//节点
template<class K>
struct BSTreeNode
{BSTreeNode* _left;BSTreeNode* _right;K _key;//构造BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};//搜索二叉树
template<class K>
class BSTree
{
public:typedef BSTreeNode<K> Node;//构造BSTree() //给定了缺省值{}//拷贝构造BSTree(const BSTree<K>& tmp){_root = Copy(tmp._root);}//operator=BSTree<K> operator=(BSTree<K> tmp){swap(_root, tmp._root);return *this;}//析构~BSTree(){Destroy(_root);}
private://递归拷贝左右子树Node* Copy(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 Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}private:Node* _root = nullptr;  //缺省值给空即可
}; 
2.1.2 插入

插入时如果为空直接插入即可,若存在节点,需要先进行判断,比根节点大的插入到它的右子树,比根节点小的插入左子树即可,这时需要注意的插入的节点需要与它的父节点进行链接,这时在往下比较的过程中就需要记录一下它的父节点。

//插入bool Insert(const K& key){//如果为空可以直接插入链接if (_root == nullptr){_root = new Node(key);return true;}//记录父节点Node* parent = nullptr;Node* cur = _root;//遍历找到合适的节点进行插入链接while (cur){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn false;}//链接cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}
2.1.3 删除

首先找到需要删除的节点,删除的时候需要注意分下面几种情况:

  • 左子树为空,可以直接删除
  • 右子树为空,可以直接删除
  • 左右子树都不为空需要使用替换法删除
  • 替换法:使用左子树最大的节点替换需要删除的节点,或者使用右子树最小的节点替换需要删除的节点,替换之后直接删除被替换的节点即可完成删除。
  • 需要注意的是在这个过程中需要记录父节点,在删除之后需要及时链接,并且要注意的是删除的节点是根节点的时候可以直接将它的左右子树直接链接。
//删除bool Erase(const K& key){Node* cur = _root;//记录父亲Node* parent = nullptr;while (cur){//找到要删除的keyif (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 (cur == parent->_left) {parent->_left = cur->_right;}else if (cur == parent->_right){parent->_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;}else if (cur == parent->_right){parent->_right = cur->_left;}}delete cur;}else    //左右子树都不为空  //使用替换法{Node* parent = cur;//右树的最小节点进行替换或者左树的最大节点Node* subRight = cur->_right;while (subRight->_left)  //找到右树的最小节点{parent = subRight;subRight = subRight->_left;}swap(cur->_key, subRight->_key);  //替换两个节点//将删除节点的右树链接在它的父亲if (parent->_left == subRight){parent->_left = subRight->_right;}else {parent->_right = subRight->_right;}delete subRight;}return true;}}return false;}
2.1.4 查找
//查找bool 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 true;}return false;}

2.2 递归版本

2.2.1 插入

递归插入时也需要进行一层封装,在里面传递root,在这里采用引用传参比较好,可以不用额外的链接,遇到空直接创建一个节点即可,直接在原数上进行操作。

//插入bool InsertR(const K& key){return _InsertR(key, _root);}bool _InsertR(const K& key, Node*& root){//树为空直接插入即可if (root == nullptr){root = new Node(key);return true;}//递归左if (root->_key > key)return _InsertR(key, root->_left);else if (root->_key < key)  //递归右return _InsertR(key, root->_right);elsereturn false;}
2.2.2 删除

还是采用里面封装一层,在递归删除的时候先递归找到要删除的key,然后判断它的左右子树,如果左右子树只存在一个可以直接进行删除,然后将它的孩子链接在它的节点上,如果左右孩子均存在,使用替换法,用该节点的右子树的最小节点进行替换,先使用循环找到该最小节点,然后与其交换,然后转化为递归该节点右子树的删除问题即可。

//删除bool EraseR(const K& key){return _EraseR(key, _root);}
bool _EraseR(const K& key, Node*& root){if (root == nullptr)return false;//查找keyif (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;delete del;return true;}else if (root->_right == nullptr)  //右为空也可以直接进行删除{Node* del = root;root = root->_left;delete del;return true;}else   //左右都不为空{Node* subRight = root;  //找到右树的最小节点while (subRight->left){subRight = subRight->_left;}swap(root->_key, subRight->_key);  //交换return _EraseR(key, root->_right);   //转化为递归右子树的子问题}}}
2.2.3 查找
//查找bool FindR(const K& key){_FindR(key, _root);}
bool _FindR(const K& key, Node* root){if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left);else if (root->_key < key)return _FindR(root->_right);elsereturn true;}
2.2.4 中序遍历

中序遍历时需要封装一层,在外面不好传递节点,中序遍历:左子树、根、右子树

	//中序遍历void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return; _InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}

3. 完整代码

#pragma once
#include <iostream>using namespace std;template<class K>
struct BSTreeNode
{BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};template<class K>
class BSTree
{
public:typedef BSTreeNode<K> Node;//构造BSTree() //给定了缺省值{}//拷贝构造BSTree(const BSTree<K>& tmp){_root = Copy(tmp._root);}//operator=BSTree<K> operator=(BSTree<K> tmp){swap(_root, tmp._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){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn false;}//链接cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}//删除bool Erase(const K& key){Node* cur = _root;//记录父亲Node* parent = nullptr;while (cur){//找到要删除的keyif (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 (cur == parent->_left){parent->_left = cur->_right;}else if (cur == parent->_right){parent->_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;}else if (cur == parent->_right){parent->_right = cur->_left;}}delete cur;}else    //左右子树都不为空  //使用替换法{Node* parent = cur;//右树的最小节点进行替换或者左树的最大节点Node* subRight = cur->_right;while (subRight->_left)  //找到右树的最小节点{parent = subRight;subRight = subRight->_left;}swap(cur->_key, subRight->_key);  //替换两个节点//将删除节点的右树链接在它的父亲if (parent->_left == subRight){parent->_left = subRight->_right;}else{parent->_right = subRight->_right;}delete subRight;}return true;}}return false;}//查找bool 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 true;}return false;}//递归版本//插入bool InsertR(const K& key){return _InsertR(key, _root);}//删除bool EraseR(const K& key){return _EraseR(key, _root);}//查找bool FindR(const K& key){_FindR(key, _root);}//中序遍历void InOrder(){_InOrder(_root);cout << endl;}private://插入bool _InsertR(const K& key, Node*& root){//树为空直接插入即可if (root == nullptr){root = new Node(key);return true;}//递归左if (root->_key > key)return _InsertR(key, root->_left);else if (root->_key < key)  //递归右return _InsertR(key, root->_right);elsereturn false;}//删除bool _EraseR(const K& key, Node*& root){if (root == nullptr)return false;//查找keyif (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;delete del;return true;}else if (root->_right == nullptr)  //右为空也可以直接进行删除{Node* del = root;root = root->_left;delete del;return true;}else   //左右都不为空{Node* subRight = root;  //找到右树的最小节点while (subRight->left){subRight = subRight->_left;}swap(root->_key, subRight->_key);  //交换return _EraseR(key, root->_right);   //转化为递归右子树的子问题}}}//查找bool _FindR(const K& key, Node* root){if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left);else if (root->_key < key)return _FindR(root->_right);elsereturn true;}//中序遍历void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}//拷贝Node* Copy(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 Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}
private:Node* _root = nullptr;
};

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!     

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

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

相关文章

深入体验:山海鲸可视化软件的独特魅力

山海鲸可视化软件是一款功能强大的数据可视化工具&#xff0c;作为该软件的资深用户&#xff0c;我深感其独特的魅力和优势。下面&#xff0c;我将从软件特点、操作体验、数据交互和实际应用场景等方面&#xff0c;为大家详细介绍山海鲸可视化软件。 首先&#xff0c;山海鲸可视…

快速入门Tailwind CSS:从零开始构建现代化界面

快速入门Tailwind CSS&#xff1a;从零开始构建现代化界面 介绍 Tailwind CSS 是一个以原子类的方式快速构建界面的 CSS 框架。它提供了丰富的预定义类&#xff0c;使得开发者能够快速构建样式和布局。 安装和设置 首先&#xff0c;我们需要在项目中安装 Tailwind CSS。可以…

MindOpt APL:一款适合优化问题数学建模的编程语言

什么是建模语言 建模语言是一种描述信息或模型的编程语言&#xff0c;在运筹优化领域&#xff0c;一般是指代数建模语言。 比如要写一个线性规划问题的建模和求解&#xff0c;可以采用C、Python、Java等通用编程语言来实现计算机编程&#xff08;码代码&#xff09;&#xff0…

3DCAT+上汽奥迪:打造新零售汽车配置器实时云渲染解决方案

在 5G、云计算等技术飞速发展的加持下&#xff0c;云渲染技术迎来了突飞猛进的发展。在这样的背景下&#xff0c;3DCAT应运而生&#xff0c;成为了业内知名的实时云渲染服务商之一。 交互式3D实时云看车作为云渲染技术的一种使用场景&#xff0c;也逐步成为一种新的看车方式&a…

如何使用 Wordpress?托管, 网站, 插件, 缓存

这是该系列教程的第一个教程&#xff0c;最终将在运行高性能 LEMP 堆栈的阿里云 ECS 实例上运行一个新的 WordPress 站点。 在本教程中&#xff0c;我们将创建一个运行 Ubuntu 16.04 的实例&#xff0c;然后通过创建超级用户并禁用 root 登录来保护服务器&#xff0c;最后配置…

PandoraFMS 监控软件 SQL注入漏洞复现

0x01 产品简介 Pandora FMS是西班牙Artica公司的一套监控系统。该系统通过可视化的方式监控网络、服务器、虚拟基础架构和应用程序等。 0x02 漏洞概述 Pandora FMS监控软件存在SQL注入漏洞,攻击者通过chart_generator.php 来执行恶意语句,获取数据库敏感信息。 0x03 复现…

【项目小结】优点分析

一、 个人博客系统 一&#xff09;限制强制登录 问题&#xff1a;限制用户登录后才能进行相关操作解决&#xff1a; 1&#xff09;前端&#xff1a; ① 写一个函数用于判断登录状态&#xff0c;如果返回的状态码是200就不进行任何操作&#xff0c;否则Ajax实现页面的跳转操作…

快速学会绘制Pyqt5中的所有图(上)

Pyqt5相关文章: 快速掌握Pyqt5的三种主窗口 快速掌握Pyqt5的2种弹簧 快速掌握Pyqt5的5种布局 快速弄懂Pyqt5的5种项目视图&#xff08;Item View&#xff09; 快速弄懂Pyqt5的4种项目部件&#xff08;Item Widget&#xff09; 快速掌握Pyqt5的6种按钮 快速掌握Pyqt5的10种容器&…

MQTT协议对比TCP网络性能测试模拟弱网测试

MQTT正常外网压测数据---时延diff/ms如下图&#xff1a; MQTT弱网外网压测数据 TCP正常外网压测数据 TCP弱网外网压测数据 结论&#xff1a; 在弱网场景下&#xff0c;MQTT和TCP的网络性能表现会有所不同。下面是它们在弱网环境中的对比&#xff1a; 连接建立&#xff1a;M…

案例064:基于微信小程序的考研论坛设计

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

Django讲课笔记01:初探Django框架

文章目录 一、学习目标二、课程导入&#xff08;一&#xff09;课程简介&#xff08;二&#xff09;课程目标&#xff08;三&#xff09;适用人群&#xff08;四&#xff09;教学方式&#xff08;五&#xff09;评估方式&#xff08;六&#xff09;参考教材 三、新课讲授&#…

24、文件上传漏洞——Apache文件解析漏洞

文章目录 一、环境简介一、Apache与php三种结合方法二、Apache解析文件的方法三、Apache解析php的方法四、漏洞原理五、修复方法 一、环境简介 Apache文件解析漏洞与用户配置有密切关系。严格来说&#xff0c;属于用户配置问题&#xff0c;这里使用ubantu的docker来复现漏洞&am…