二叉搜索树(C++)

二叉搜索树

  • 概念
  • 二叉搜索树的应用
  • 二叉搜索树的实现
    • K模型
      • 基本结构和函数声明
      • 接口实现
        • ①find——查找关键码
        • ②Insert——插入关键码
        • ③Erase——删除关键码(==重点==)
        • 时间复杂度
      • 源码(整体)
        • 非递归
        • 递归
    • KV模型

在使用C语言写数据结构阶段时,对二叉树进行了讲解。本节内容是对二叉树的深入探索,也是二叉树部分的收尾

概念

二叉搜索树也称二叉排序树(BST,Binary Search Tree):

  1. 空树
  2. 非空树(要具有以下性质)
    • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
    • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
    • 它的左右子树也分别为二叉搜索树

注意: 二叉搜索树的key值互不相同

eg:下图就属于二叉搜索树
二叉搜索树

二叉搜索树的应用

  • K模型:即只有key作为关键码,结构中只需要存储key。关键码就是要搜索到的值

     eg: 给一个单词word,判断该单词是否拼写正确?具体步骤:1. 以词库中所有单词为基础,每个单词都是key,构建一颗搜索二叉树2. 在二叉树中搜索该单词是否存在,存在则返回true,否则返回false
    
  • KV模型:每一个关键码key,都有与之对应的value,即<Key, Value>的键值对

     eg1: 英汉词典 - 就是中文与英文的对应关系。通过英文可以快速找到与其对应的中文,英文单词和与其对应的中文<word, chinese>就构成键值对。eg2: 统计单词次数,统计成功后,给定单词就可以找到单词出现得次数。单词与其出现次数就是<word, count>就构成一种键值对
    

二叉搜索树的实现

注意: 通过二叉搜索树的应用,了解到其有两个模型,接下来对这两个模型分别进行实现。因为两个模型的接口都相似,所以仅对一个模型的接口进行详细介绍。

K模型

K模型的实现分为递归和非递归两种,但是接口的实现逻辑是一样的。为了便于理解把基本结构和函数声明先附上,然后对接口进行讲解,最后在贴上完整的源码

基本结构和函数声明

//二叉树节点
//节点使用struct,默认public访问
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():_root(nullptr){}//拷贝构造BSTree(const BSTree<K>& t);//赋值运算符重载BSTree<K>& operator=(BSTree<K> t);//析构函数~BSTree();//插入bool Insert(const K& key);//查找bool Find(const K& key);//删除bool Erase(const K& key);//中序遍历void InOrder();private:Node* _root;
};

接口实现

①find——查找关键码

功能:查找关键码是否在树中,是返回真,否则返回假。
原理:

  1. 从根开始比较,比根大则往右边查找,比根小往左边查找
  2. 最多查找高度次,走到nullptr,则这个值不存在

实现代码

  1. 非递归版本
template<class K>
bool BSTree<K>::Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}else{return true;}}return false;
}
  1. 递归版本
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;}else{return true;}}return false;
}

②Insert——插入关键码

原理:

  1. 树为空,直接把要插入的节点赋值给root
  2. 树不为空,通过查找的思路,找到要插入的合适位置。插入成功返回true
    注意:如果要插入的值和树中的值冲突,则返回false

实现代码

  1. 非递归版本
//插入
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->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key);if (parent->_key > key){parent->_left = cur;}else{parent->_right = cur;}return true;
}
  1. 递归版本
bool InsertR(const K& key)
{return _InsertR(_root, key);
}
bool _FindR(Node* root, const K& key)
{if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left, key);else if (root->_key < key)return _FindR(root->_right, key);elsereturn true;
}

③Erase——删除关键码(重点

原理:

  1. 查找关键码是否在二叉搜索树中,不存在直接返回false
  2. 存在,则要分为四种情况

情况:
在考虑树的所有情况时,要把根节点的情况和普通情况,分离开来。哪怕最后根节点的情况和普通情况一样。(仅代表博主个人观点)

  1. 要删除的节点无孩子节点
    第一种情况
  1. 要删除的节点无左孩子节点
    第二种情况
  1. 要删除的节点无右孩子节点
    第三种情况
  1. 要删除的节点左右都不为空 (采用的方法 一 替换法
    替换法:找到删除节点左子树的最大值节点(leftMax),然后与删除节点交换,再删除现在的leftMax。(也可以找删除节点右子树的最小值节点(rightMin),原理相同)
    第四种情况

实现代码

  1. 非递归版本
//删除
bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else  //找到了要删除的值{//分情况//1.左为空if (cur->_left == nullptr){if (cur == _root)_root = cur->_right;else{if (parent->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}//2.右为空else if (cur->_right == nullptr){if (cur == _root)_root = cur->_left;else{if (parent->_right == cur){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}} //3.左右都不为空else{parent = cur;Node* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(cur->_key, leftMax->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur = leftMax;}delete cur;cur = nullptr;return true;}}return false;
}
  1. 递归版本
//删除
bool EraseR(const K& key)
{return _EraseR(_root, key);
}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{Node* del = root;if (root->_left == nullptr)root = root->_right;else if (root->_right == nullptr)root = root->_left;else{Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;del = nullptr;return true;}
}

时间复杂度

二叉搜索树的操作时间复杂度:在O(logN)和O(N)之间
画图解释时间复杂度:
时间复杂度

源码(整体)

非递归

//非递归
namespace key
{//二叉树节点//节点使用struct,默认public访问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():_root(nullptr){}//拷贝构造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->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key);if (parent->_key > key){parent->_left = cur;}else{parent->_right = cur;}return true;}//查找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;}else{return true;}}return false;}//删除bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else  //找到了要删除的值{//分情况//1.左为空if (cur->_left == nullptr){if (cur == _root)_root = cur->_right;else{if (parent->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}//2.右为空else if (cur->_right == nullptr){if (cur == _root)_root = cur->_left;else{if (parent->_right == cur){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}} //3.左右都不为空else{parent = cur;Node* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(cur->_key, leftMax->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur = leftMax;}delete cur;cur = nullptr;return true;}}return false;}//中序遍历void InOrder(){_InOrder(_root);cout << endl;}private:Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* copyRoot = new Node(root->_key);copyRoot->_left = Copy(root->_left);copyRoot->_right = Copy(root->_tight);return copyRoot;}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);}private:Node* _root;};
}

递归

//递归
namespace keyR
{//二叉树节点//节点使用struct,默认public访问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():_root(nullptr){}//拷贝构造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 InsertR(const K& key){return _InsertR(_root, key);}//查找bool FindR(const K& key){return _FindR(_root, key);}//删除bool EraseR(const K& key){return _EraseR(_root, key);}//中序遍历void InOrder(){_InOrder(_root);cout << endl;}private://在root添加引用很关键,要不然得是二级指针bool _InsertR(Node*& root, const K& key){if (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);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{Node* del = root;if (root->_left == nullptr)root = root->_right;else if (root->_right == nullptr)root = root->_left;else{Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;del = nullptr;return true;}}bool _FindR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left, key);else if (root->_key < key)return _FindR(root->_right, key);elsereturn true;}void Destroy(Node*& root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* copyRoot = new Node(root->_key);copyRoot->_left = CopyRoot(root->_left);copyRoot->_right = CopyRoot(root->_right);return copyRoot;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root;};
}

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:BSTree():_root(nullptr){}BSTree(const BSTree<K, V>& t){_root = Copy(t._root);}BSTree<K, V>& operator=(BSTree<K, V> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}void InOrder(){_InOrder(_root);cout << endl;}Node* FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key, const V& value){return _InsertR(_root, key, value);}bool EraseR(const K& key){return _EraseR(_root, key);}private: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{Node* del = root;if (root->_left == nullptr){//这个root的引用,非常好,root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);//删的值在左边return _EraseR(root->_left, key);}delete del;return true;}}void Destroy(Node*& root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* copyRoot = new Node(root->_key, root->_value);copyRoot->_left = Copy(root->_left);copyRoot->_right = Copy(root->_right);return copyRoot;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}Node* _FindR(Node* root, const K& key){if (root == nullptr)return nullptr;if (root->_key > key){return _FindR(root->_left, key);}else if (root->_key < key){return _FindR(root->_right, key);}else{return root;}}bool _InsertR(Node*& root, const K& key, const V& value){if (root == nullptr){root = new Node(key, value);return true;}if (root->_key > key){return _InsertR(root->_left, key, value);}else if (root->_key < key){return _InsertR(root->_right, key, value);}else{return false;}}private:Node* _root;};}

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

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

相关文章

Leetcode:【448. 找到所有数组中消失的数字】题解

题目 给你一个含 n 个整数的数组 nums &#xff0c;其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字&#xff0c;并以数组的形式返回结果。 难度&#xff1a;简单 题目链接&#xff1a;448. 找到所有数组中消失的数字 示例1 输入&…

.net framework 提示安装了 但是删除面板看不到

如果你在计算机上安装了.NET Framework&#xff0c;但在“控制面板”中找不到.NET Framework的相关条目&#xff0c;可能是因为.NET Framework的某些组件或特定版本未在“程序和功能”&#xff08;或旧版本的Windows中称为“程序和功能”&#xff09;列表中列出。这可能是正常情…

Mybatis1.1 环境准备

1.1 环境准备 数据库表&#xff08;tb_brand&#xff09;及数据准备实体类 Brand编写测试用例安装 MyBatisX 插件 数据库表&#xff08;tb_brand&#xff09;及数据准备 -- 删除tb_brand表 drop table if exists tb_brand; -- 创建tb_brand表 create table tb_brand (-- id 主…

C语言每日一练------Day(6)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;整数转换 异或 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn…

第62步 深度学习图像识别:多分类建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 上期我们基于TensorFlow环境做了图像识别的多分类任务建模。 本期以健康组、肺结核组、COVID-19组、细菌性&#xff08;病毒性&#xff09;肺炎组为数据集&#xff0c;基于Pytorch环境&#xff0c;构建SqueezeNet多分类模型&#xff0…

VBA:Application.GetOpenFilename打开指定文件夹里的excel类型文件(xls、xlsx)

GetOpenFilename相当于Excel打开窗口&#xff0c;通过该窗口选择要打开的文件&#xff0c;并可以返回选择的文件完整路径和文件名。 Application.GetOpenFilename(“文件类型筛选规则(就是说明)”,“优先显示第几个类型的文件”,“标题”,“是否允许选择多个文件名”) 打开类型…

【安装包】JDK 17安装教程

软件下载 软件&#xff1a;JDK版本&#xff1a;17语言&#xff1a;简体中文大小&#xff1a;151.24M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baidu.com/…

【12期】谈一谈redis两种持久化机制的区别?

Redis两类持续性的方法 RDB方案可以在规定时间间隔内创建数据集的时间点快照。 AOF方案记录了服务器执行的所有写操作命令&#xff0c;并在服务器启动时通过重新执行这些命令来还原数据集。AOF文件完全遵循Redis协议格式保存&#xff0c;新命令会被追加到文件末尾。此外&#…

cv2实现.jpeg转.jpg

import cv2 import os path1 /home/uto/FCF/ path2 /home/uto/FCF1/def convert(path1,path2):for filename in os.listdir(path1): #os.listdir()用于返回包含指定文件夹的名i的列表if filename.endswith(".jpeg"): #获取后缀名img cv2.imread(os.path.joi…

【爬虫】5.5 Selenium 爬取Ajax网页数据

目录 AJAX 简介 任务目标 创建Ajax网站 创建服务器程序 编写爬虫程序 AJAX 简介 AJAX&#xff08;Asynchronous JavaScript And XML&#xff0c;异步 JavaScript 及 XML&#xff09; Asynchronous 一种创建交互式、快速动态网页应用的网页开发技术通过在后台与服务器进行…

软考:中级软件设计师:邮件加密系统,网络安全保障,网络威胁与攻击,防火墙技术

软考&#xff1a;中级软件设计师:邮件加密系统 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的 &…

前端食堂技术周刊第 96 期:2023 CSS 状态、Nuxt 3.7、TypeScript 5.2、eBay 性能优化、贝塞尔曲线

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;冰镇黑乌龙 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看…

【Linux-Day8- 进程替换和信号】

进程替换和信号 问题引入 我们发现 终端输入的任意命令的父进程都是bash,这是因为Linux系统是用fork()复制出子进程&#xff0c;然后在子进程中调用替换函数进行进程替换&#xff0c;实现相关命令。 &#xff08;1&#xff09; exec 系列替换过程&#xff1a;pcb 使用以前的只…

阿里云申请免费SSL证书的两种验证方式及配置服务器Tomcat升级HTTPS协议

通用教程&#xff0c;其他服务商的免费 SSL 证书也差不多是这个流程。&#xff08;至少腾讯云的操作步骤和本文是一致&#xff0c;嘻嘻&#xff01;&#xff09; 申请 SSL 证书 首先在阿里云上创建并申请 SSL 证书&#xff0c;之后选择 DNS 验证的方式&#xff0c;一种是手动配…

微信小程序 scroll-view 组件的 bindscroll 不触发不生效

使用微信小程序基础组件中的scroll-view&#xff0c;但是滑动的时候 bindscroll 一直不生效。 <view class"container log-list"><scroll-view scroll-y style"height:100%;white-space:nowrap;" scroll-into-view"{{toView}}" enable…

【算法】经典的八大排序算法

点击链接 可视化排序 动态演示各个排序算法来加深理解&#xff0c;大致如下 一&#xff0c;冒泡排序&#xff08;Bubble Sort&#xff09; 原理 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它通过多次比较和交换相邻元素的方式&#xff0c;将…

springcloud-nacos简述

Spring Cloud alibaba: nacos服务注册中心&#xff0c;配置中心 服务注册中心 1.项目父工程添加springcloudalibaba依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><ve…

Ubuntu20以上高版本如何安装低版本GCC

安装了Ubuntu 20.04之后&#xff0c;通过命令行 sudo apt-get install build-essential安装gcc&#xff0c;再通过命令行 gcc -v可查看gcc版本为gcc13 如果想用低版本的gcc&#xff0c;比如gcc4.8&#xff0c;尝试输入命令 sudo apt-get install gcc-4.8会提示找不到gcc4.8的…

context.WithCancel()的使用

“ WithCancel可以将一个Context包装为cancelCtx,并提供一个取消函数,调用这个取消函数,可以Cancel对应的Context Go语言context包-cancelCtx[1] 疑问 context.WithCancel()取消机制的理解[2] 父母5s钟后出门&#xff0c;倒计时&#xff0c;父母在时要学习&#xff0c;父母一走…

代码随想录算法训练营Day45 | 70. 爬楼梯 (进阶) | 322. 零钱兑换 | 279. 完全平方数

文章目录 70. 爬楼梯 (进阶)322. 零钱兑换二维数组滚动数组 279. 完全平方数 70. 爬楼梯 (进阶) 题目链接 | 理论基础 以完全背包的思路来解题&#xff0c;正如组合总和 Ⅳ 中提到的一样。在本题中&#xff0c;先背包后物品的思路就显得非常合理明显了。 本题中的物品就是可…