C++------利用C++实现二叉搜索树【数据结构】

文章目录

  • 二叉搜索树
    • 概念
    • 二叉搜索树的操作
      • 查找
      • 插入
      • 删除
    • 二叉搜索树的应用

二叉搜索树

概念

什么是二叉搜索树,二叉搜索树就是指左孩子永远比根小右孩子永远比根大。这个规则适用于所有的子树。
在这里插入图片描述
上面的就是一棵二叉搜索树,我们还可以发现这棵树走一个中序遍历序列是有序的,所以它又被称为二叉排序树。

二叉搜索树的操作

二叉搜索树的操作主要分为以下几点,查找, 插入,删除。

查找

算法思想:二叉搜索树的查找算法是这样的,从根的地方开始找,如果要找的key比根大就到右子树去找,如果比根小就到左子树找。时间复杂度最差为O(N)最优为O(logn),如果一棵树走完了还没有找到说明这个数字不在这棵树内。
O(N)时间复杂度是下面这棵树的查找产生的。
在这里插入图片描述
我们要使树的高度保持为logN就必须引入平衡二叉搜索树的概念,这个部分我们在后面的AVL和红黑树部分讲解。
非递归算法:

	bool Find(const K& key){Node* cur = _root;while (cur != nullptr){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}else{return true;}}return false;}

递归算法:
key小于就递归找左树,大于就递归找右树

	bool _FindR(Node* root, const K& key){if (root == nullptr){return false;}if (root->_key < key){_FindR(root->_right, key);}else if (root->_key > key){_FindR(root->_left, key);}else{return true;}}

插入

插入分为两种情况:
1、如果树为空,则直接新增节点,赋值给_root指针。
2、树不为空,按二叉搜索树性质查找插入位置,插入新节点。
在这里插入图片描述
非递归算法

bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* parent = nullptr;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;}

递归算法

		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);}else{return false;}}

删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回,否则要删除的节点可能分下面四种情况:
a.要删除的节点无孩子节点
b.要删除的节点只有左孩子节点
c.要删除的节点只有右孩子节点
d.要删除的节点有左右孩子节点
情况b,c可以合成一个情况,那就是只有一个孩子节点。
情况b:删除该节点且是被删除节点的双亲节点指向被删除节点的左孩子节点—直接删除
在这里插入图片描述

情况c:删除该节点且是被删除节点的双亲节点指向被删除节点的右孩子节点—直接删除
在这里插入图片描述

情况d:在它的左子树中寻找最大节点,用它的值填补到被删除节点中,再来处理该节点的删除问题—替换法删除。
在这里插入图片描述
非递归算法:

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->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}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;}}}else{//左右都不为空//找到左树中的最大值为替代节点Node* parent = cur;Node* leftmax = _root->_left;while (leftmax->_right != nullptr){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;return true;}}return false;}

非递归算法:

//Node*& 引用是关键,没有引用就不会链接起来,不用引用也可以用二级指针。
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;//1、左为空//2、右为空//3、左右都不为空if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* leftMax = root->_left;while (leftMax->_left){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;return true;}}

完整代码

namespace name
{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(){Destory(_root);}bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* parent = nullptr;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;}bool Find(const K& key){Node* cur = _root;while (cur != nullptr){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* 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->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}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;}}}else{//左右都不为空//找到左树中的最大值为替代节点Node* parent = cur;Node* leftmax = _root->_left;while (leftmax->_right != nullptr){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;return true;}}return false;}void Inorder(){_Inorder(_root);cout << endl;}bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}private:void Destory(Node* root){if (root == nullptr){return;}Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}Node* Copy(Node* root){if (root == nullptr){return nullptr;}Node* copyroot = new Node(root->_key);copyroot->_left = Copy(root->_left);copyroot->_right = Copy(root->_right);return copyroot;}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);}else{return false;}}bool _FindR(Node* root, const K& key){if (root == nullptr){return false;}if (root->_key < key){_FindR(root->_right, key);}else if (root->_key > key){_FindR(root->_left, key);}else{return true;}}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;//1、左为空//2、右为空//3、左右都不为空if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* leftMax = root->_left;while (leftMax->_left){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;return true;}}void _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}Node* _root;};
}

二叉搜索树的应用

应用主要是分为了K模型和KV模型,后面的set为K模型,map为KV模型,具体是这样的:

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到 的值。 比如:给一个单词word,判断该单词是否拼写正确,具体方式如下: 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
    在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见: 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英 文单词与其对应的中文<word,chinese>就构成一种键值对; 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word,count>就构成一种键值对。
    上面的代码时K模型的,下面的代码时KV模型的。
namespace name1
{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(){Destory(_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:void Destory(Node* root){if (root == nullptr){return;}Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}Node* Copy(Node* root){if (root == nullptr){return nullptr;}Node* copyroot = new Node(root->_key);copyroot->_left = Copy(root->_left);copyroot->_right = Copy(root->_right);return copyroot;}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;}}Node* _FindR(Node* root, const K& key){if (root == nullptr){return nullptr;}if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return root;}}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;//1、左为空//2、右为空//3、左右都不为空if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* leftMax = root->_left;while (leftMax->_left){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;return true;}}void _Inorder(Node* root){if (root==nullptr){return;}_Inorder(root->_left);cout << root->_key << ":" << root->_value << endl;_Inorder(root->_right);}Node* _root;};
}

KV模型的测试:

void Test_BSTree7()
{string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };name1::BSTree<string, int> countTree;for (auto& str : arr){//没有找到证明是第一次出现auto ret = countTree.FindR(str);if (ret == nullptr){countTree.InsertR(str, 1);}else{ret->_value++;}}countTree.Inorder();
}
int main()
{//Test_BSTree();//Test_BSTree1();//Test_BSTree2();//Test_BSTree3();//Test_BSTree4();//Test_BSTree5();//Test_BSTree6();Test_BSTree7();return 0;
}

在这里插入图片描述
好的我们下一篇再见!

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

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

相关文章

英伟达结构化剪枝工具Nvidia Apex Automatic Sparsity [ASP](1)——使用方法

英伟达结构化剪枝工具Nvidia Apex Automatic Sparsity [ASP]&#xff08;1&#xff09;——使用方法 Apex是Nvdia维护的pytorch工具库&#xff0c;包括混合精度训练和分布式训练&#xff0c;Apex的目的是为了让用户能够更早的使用上这些“新鲜出炉”的训练工具。ASP&#xff0…

块设备驱动模板

内核版本&#xff1a;4.14.0 基于设备树 使用请求队列&#xff0c;请求队列会用到I/O调度器&#xff0c;适合机械硬盘这种存储设备。 #include <linux/module.h> #include <linux/blkdev.h> #include <linux/hdreg.h> #define RAMDISK_SIZE (2*1024*1024…

Windows安装 Elasticsearch 教程

下载地址 Past Releases of Elastic Stack Software | Elastic 解压 解压完的样子 进入BIN目录 D:\Develop\elasticsearch\elasticsearch-7.12.0\bin 按住shift 鼠标右键 打开 powershell 窗口 查看ES版本 .\elasticsearch.bat --version 出现问题了 警告&#xff1a;不赞成…

如何学习专业的学术用语01

问题的提出——凭啥人家写的词汇这么专业 做法一 做法二&#xff1a;做一个专业数据库 专门做教育技术类的

React(6)

1.React插槽 import React, { Component } from react import Child from ./compoent/Childexport default class App extends Component {render() {return (<div><Child><div>App下的div</div></Child></div>)} }import React, { Compon…

(二)结构型模式:4、组合模式(Composite Pattern)(C++实例)

目录 1、组合模式&#xff08;Composite Pattern&#xff09;含义 2、组合模式应用场景 3、组合模式的优缺点 4、组合模式的UML图学习 5、C实现组合模式的简单示例&#xff08;公司的OA系统&#xff09; 1、组合模式&#xff08;Composite Pattern&#xff09;含义 组合模…

2022寒假牛客训练4

G-子序列权值乘积 设计知识&#xff1a; 欧拉降幂 如果我们要求ab%p 而b是一个很大的数&#xff0c;可以先将b对p-1取模&#xff0c;不会影响结果的正确性&#xff0c;前提是a和p互质。 这一题我们可以将数字先进行排序&#xff0c;因为子序列中只有最大和最小的才会被计算&am…

Hyper-V 扩展虚拟磁盘后,如何扩容到 /ubuntu--vg-ubuntu--lv

创建虚拟机的时候&#xff0c;一般选择 动态扩展虚拟硬盘&#xff0c;N 多年以来&#xff0c;一直没有关心过他是如何动态扩展的&#xff0c;直到最近折腾大文件 SQL 导入任务&#xff0c;遇到了磁盘空间占满的情形 以下这 2 个就体现了动态扩展空间&#xff0c;扩展起来很容易…

Visual Studio 如何放大代码字体的大小

1.打开Visual Studio&#xff0c;新建一个程序&#xff0c;一段代码&#xff0c;为接下去的操作做好准备。单击菜单栏的【工具】选项。 2.在跳出来菜单中找到【选项】&#xff08;一般在最后一项&#xff09;&#xff0c;然后单击。跳出新的窗口。 3.跳出新的窗口后&#xff…

Leetcode 0814周总结

本周刷题&#xff1a; 88, 108, 121, 219, 228, 268, 283, 303, 349, 350, 414, 448 88 合并两个有序数组 nums1{1, 2, 3 ,0, 0, 0} nums2{2, 5, 6} 合成效果&#xff1a;nums1{1, 2, 2, 3, 5, 6} 思路&#xff1a;【双指针】对两个数组设置双指针&#xff0c;依次比较哪…

无涯教程-TensorFlow - 单词嵌入

Word embedding是从离散对象(如单词)映射到向量和实数的概念&#xff0c;可将离散的输入对象有效地转换为有用的向量。 Word embedding的输入如下所示: blue: (0.01359, 0.00075997, 0.24608, ..., -0.2524, 1.0048, 0.06259) blues: (0.01396, 0.11887, -0.48963, ..., 0.03…

C++数组初始化

在C中&#xff0c;bool a[5]{} 和 bool a[5] 之间存在一些不同。 bool a[5]{}&#xff1a; 这将会初始化数组 a 的所有元素为 false&#xff0c;因为在初始化列表中没有提供任何值&#xff0c;编译器会自动将数组的所有元素初始化为其对应数据类型的默认值。对于布尔型数据&…

【Docker】Docker安装 MySQL 8.0,简洁版-快速安装使用

今天&#xff0c;使用docker安装mysql数据库进行一个测试&#xff0c;结果网上找了一篇文章&#xff0c;然后。。。。坑死我… 特总结本篇安装教程&#xff0c;主打一个废话不多说&#xff01; 坑&#xff1a;安装成功&#xff0c;客户端工具连接不上数据库》。。。 正文&…

【蓝桥杯】[递归]母牛的故事

原题链接&#xff1a;https://www.dotcpp.com/oj/problem1004.html 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 我们列一个年份和母牛数量的表格&#xff1a; 通过观察&#xff0c;找规律&#xff0c;我们发现&#xff1a; 当年份小于等于4时&…

js 小程序限流函数 return闭包函数执行不了

问题&#xff1a; 调用限流 &#xff0c;没走闭包的函数&#xff1a; checkBalanceReq&#xff08;&#xff09; loadsh.js // 限流 const throttle (fn, context, interval) > {console.log(">>>>cmm throttle", context, interval)let canRun…

五、二维费用的背包问题

五、二维费用的背包问题 题记算法题目代码 题记 二维费用的背包问题是指在选择物品放入背包时&#xff0c;每个物品有两个不同的费用&#xff0c;且背包的容量也有限制。目标是在保证费用不超过限制的前提下&#xff0c;使得放入背包的物品价值最大化。 算法 费用加了一维&a…

〔011〕Stable Diffusion 之 解决绘制多人或面部很小的人物时面部崩坏问题 篇

✨ 目录 🎈 脸部崩坏🎈 下载脸部修复插件🎈 启用脸部修复插件🎈 插件生成效果🎈 插件功能详解🎈 脸部崩坏 相信很多人在画图时候,特别是画 有多个人物 图片或者 人物在图片中很小 的时候,都会很容易出现面部崩坏的问题这是由于神经网络无法完全捕捉人脸的微妙细节…

opencv进阶09-视频处理cv2.VideoCapture示例(打开本机电脑摄像头)

视频信号&#xff08;以下简称为视频&#xff09;是非常重要的视觉信息来源&#xff0c;它是视觉处理过程中经常要处理的一类信号。实际上&#xff0c;视频是由一系列图像构成的&#xff0c;这一系列图像被称为帧&#xff0c;帧是以固定的时间间隔从视频中获取的。获取&#xf…

透视俄乌网络战之一:数据擦除软件

数据擦除破坏 1. WhisperGate2. HermeticWiper3. IsaacWiper4. WhisperKill5. CaddyWiper6. DoubleZero7. AcidRain8. RURansom 数据是政府、社会和企业组织运行的关键要素。数据擦除软件可以在不留任何痕迹的情况下擦除数据并阻止操作系统恢复摧&#xff0c;达到摧毁或目标系统…

Python学习笔记_进阶篇(四)_django知识(三)

本章内容&#xff1a; Django 发送邮件Django cookieDjango sessionDjango CSRF Django 发送邮件 我们常常会用到一些发送邮件的功能&#xff0c;比如有人提交了应聘的表单&#xff0c;可以向HR的邮箱发邮件&#xff0c;这样&#xff0c;HR不看网站就可以知道有人在网站上提…