二叉搜索树 --- C++实现

目录

1.二叉搜索树的概念

2.二叉搜索树的操作

3. 二叉树的实现

4.二叉搜索树的应用

5. 二叉树的性能分析

6. 二叉树进阶练习题


1.二叉搜索树的概念

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

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

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

2.二叉搜索树的操作

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

1. 二叉搜索树的查找

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

2. 二叉搜索树的插入

    插入的具体过程如下:

  • a. 树为空,则直接新增节点,赋值给root指针
  • b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
     

3. 二叉搜索树的删除

        首先查找元素是否在二叉搜索树中,如果不存在,则返回,否则要删除的结点可能分下面四种情况:

  • a. 要删除的结点无孩子结点
  • b. 要删除的结点只有左孩子结点
  • c. 要删除的结点只有右孩子结点
  • d. 要删除的结点有左、右孩子结点

        看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,把一个孩子看作空节点,因此真正的删除过程如下:

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点,然后直接删除该节点 -- 即直接删除。
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点,然后直接删除该节点 -- 即直接删除。
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题 -- 替换法删除。即用左子树的最大节点,或右子树的最小节点来替换。

3. 二叉树的实现

代码中有每个操作都有两种写法,一种是非递归写法,一种是递归写法。 

namespace key
{template<class K>struct BSTreeNode{BSTreeNode<K>* left;BSTreeNode<K>* right;K _key;BSTreeNode(const K& key = K()):left(nullptr), right(nullptr), _key(key){}};template<class K>class BSTree{typedef BSTreeNode<K> Node;public:BSTree():_root(nullptr){}//C++11 强制生成默认构造//BSTree() = default; BSTree(const BSTree<K>& root){_root = Copy(root._root);}BSTree<K>& operator=(BSTree<K> tree){swap(tree._root, _root);return *this;}bool Insert(const K& key)//插入{if (_root == nullptr){_root = new Node(key);return true;}else{Node* cur = _root;Node* parent = nullptr;while (cur){parent = cur;if (cur->_key > key){cur = cur->left;}else if (cur->_key < key){cur = cur->right;}else{return false;}}cur = new Node(key);if (cur->_key < parent->_key){parent->left = cur;}else{parent->right = cur;}return true;}}bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key == key){return true;}else if (cur->_key < key){cur = cur->right;}else{cur = cur->left;}}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 (parent == nullptr)//根节点{_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 (parent == nullptr)//根节点{_root = cur->left;}else{if (cur == parent->left){parent->right = cur->left;}else if (cur == parent->right){parent->right = cur->left;}delete cur;}}else{//左右都不为空//右树的最小节点(最左节点)Node* subLeft = cur->right;Node* parent = cur;while (subLeft->left){parent = subLeft;subLeft = subLeft->left;}if (subLeft == cur->right)//右树的最左节点是根,最左节点不是父节点的左孩子{swap(subLeft->_key, cur->_key);parent->right = subLeft->right;}else{swap(subLeft->_key, cur->_key);parent->left = subLeft->right;}delete subLeft;}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);}~BSTree(){Destroy(_root);}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)//auto不能做函数参数{if (root == nullptr){return;}Destroy(root->left);Destroy(root->right);delete root;root = nullptr;}bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}if (root->_key > key){return _EraseR(root->left, key);}if (root->_key < key){return _EraseR(root->right, key);}else{//删除,用引用十分巧妙if (root->right == nullptr){//引用的是父节点的一个指针Node* del = root;root = root->left;delete del;return true;}else if (root->left == nullptr){Node* del = root;root = root->right;delete del;return true;}else{Node* subLeft = root->right;while (subLeft->left){subLeft = subLeft->left;}swap(subLeft->_key, root->_key);//让子树递归删除 return _EraseR(root->right, key);}}}bool _InsertR(Node*& root, const K& key){//这里root用引用if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){//引用的是父节点的右孩子_InsertR(root->right, key);}if (root->_key > key){_InsertR(root->left, key);}else{return false;}}//bool _InsertR(Node* root, const K& key)//{//	if (root == nullptr)//	{//		_root = new Node(key);//		return true;//	}//	if (key == root->_key)//	{//		return false;//	}//	if (key < root->_key)//	{//		if (root->left == nullptr)//		{//			root->left = new Node(key);//			return true;//		}//		else//		{//			return _InsertR(root->left, key);//		}//	}//	if (key > root->_key)//	{//		if (root->right == nullptr)//		{//			root->right = new Node(key);//			return true;//		}//		else//		{//			return _InsertR(root->right, key);//		}//	}//}bool _FindR(Node* root, const K& key){if (root == nullptr){return false;}else if (root->_key == key){return true;}else if (root->_key < key){return _FindR(root->right, key);}else{return _FindR(root->left, key);}}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->left);cout << root->_key << " ";_InOrder(root->right);}private:Node* _root = nullptr;};
}

4.二叉搜索树的应用

1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

  1. 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  2. 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:

  1. 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
  2. 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对
     
//改造后的 key_value 结构
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 = K(),const V& value = V()):left(nullptr), right(nullptr), _key(key), _value(value){} };template<class K, class V>class BSTree{typedef BSTreeNode<K,V> Node;public:bool Insert(const K& key,const V& value){if (_root == nullptr){_root = new Node(key,value);return true;}else{Node* cur = _root;Node* parent = nullptr;while (cur){parent = cur;if (cur->_key > key){cur = cur->left;}else if (cur->_key < key){cur = cur->right;}else{return false;}}cur = new Node(key,value);if (cur->_key < parent->_key){parent->left = cur;}else{parent->right = cur;}return true;}}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key == key){return cur;}else if (cur->_key < key){cur = cur->right;}else{cur = cur->left;}}return nullptr;}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 (parent == nullptr)//根节点{_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 (parent == nullptr)//根节点{_root = cur->left;}else{if (cur == parent->left){parent->right = cur->left;}else if (cur == parent->right){parent->right = cur->left;}delete cur;}}else{//左右都不为空//右树的最小节点(最左节点)Node* subLeft = cur->right;Node* parent = cur;while (subLeft->left){parent = subLeft;subLeft = subLeft->left;}if (subLeft == cur->right)//右树的最左节点是根,最左节点不是父节点的左孩子{swap(subLeft->_key, cur->_key);parent->right = subLeft->right;}else{swap(subLeft->_key, cur->_key);parent->left = subLeft->right;}delete subLeft;}return true;}}return false;}//递归遍历void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->right);}private:Node* _root = nullptr;};
}

5. 二叉树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其时间复杂度为:O(logN)

最差情况下,二叉搜索树退化为单支树(或者类似单支),其时间复杂度为:O(N)

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续文章学习的AVL树和红黑树就可以上场了。

6. 二叉树进阶练习题

这些题目更适合使用C++完成,难度也更大一些

  1. 二叉树创建字符串。OJ链接
  2. 二叉树的分层遍历1。OJ链接
  3. 二叉树的分层遍历2。OJ链接
  4. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。OJ链接
  5. 二叉树搜索树转换成排序双向链表。OJ链接
  6. 根据一棵树的前序遍历与中序遍历构造二叉树。OJ链接
  7. 根据一棵树的中序遍历与后序遍历构造二叉树。OJ链接
  8. 二叉树的前序遍历,非递归迭代实现 。OJ链接
  9. 二叉树中序遍历 ,非递归迭代实现。OJ链接
  10. 二叉树的后序遍历 ,非递归迭代实现。OJ链接

本篇详细代码可查看我的Gitee

本篇结束!

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

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

相关文章

JDBC 知识点总结篇

JDBC 知识点总结篇 JDBC 接口 Java DataBase Connectivity Java数据库连接&#xff0c;由官方定义的一套操作所有关系型数据库的规则&#xff0c;即接口&#xff0c;各个数据库厂商实现该套接口 代码 // 本代码只提供一个样例&#xff0c;请根据自己实际情况修改代码 // 1.…

MyBatis笔记

Mybatis Mybatis介绍 什么是Mybatis? mybatis是支持普通SQL查询、存储过程和高级映射的优秀持久层框架。 Mybatis优点 几乎消除了JDBC代码和参数的手动设置消除结果集的检索使用XML或注解用于配置和原始映射&#xff0c;将接口和POJOs(实体类)映射成数据库中的记录。 My…

vue微乾坤子应用开发及ele组件开发时问题记录

一. 微乾坤 1. 新增page页面路由,pmi权限中心配置正常&#xff0c;跳转链接正确&#xff0c;但路由未找到403. 解决&#xff1a; 新增的配置是page类型&#xff0c;transformQianKunRoute方法转换微前端路由数据 时&#xff0c;过滤未兼容page型的路由&#xff0c; 解决 [menu,…

react中使用redux最简单最方便的方式,配合rematch简化操作,5分钟学会

react中使用状态管理的方式也很多&#xff0c;比如redux和mobx等&#xff0c;今天这一片就讲一下redux的入门到熟练使用&#xff0c;主要是要理解它redux的组成有哪些&#xff0c;到怎么创建&#xff0c;和组建中怎么使用三个问题。这里先放上官网文档&#xff0c;不理解的地方…

十一.约束(二)

约束 5.自增列:AUTO_INCREMENT5.1作用5.2关键字5.3特点和要求5.4如何指定自增约束5.5如何删除自增列5.6MySQL8.0新特性——自增变量的持久化 6.FOREIGN KEY 约束6.1作用6.2关键字6.3主表和从表/父表和子表6.4特点6.5添加外键约束6.6演示问题6.7约束等级6.8删除外键约束6.9开发场…

鸿蒙开发者工具安装及入门程序

下载工具DevEco Studio IDE 官网下载&#xff1a;HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 开发工具的安装 解压下载好的压缩包&#xff0c;一路无脑安装即可&#xff0c;安装完的使用方法类似于IDEA、WebStorm的使用&#xff0c;快捷键一致&#xff0c;默认黑…

【笔记】Spring的循环依赖

Spring的循环依赖 ObjectFactory:函数式接口&#xff0c;可以将lambda表达式作为参数放在方法的实参种&#xff0c;在方法执行的时候&#xff0c;并不会实际的调用当前lambda表达式&#xff0c;只有在调用getObject方法的时候才回去调用lambda表达式 为什么spring要用三级缓存…

常用的百兆网络变压器与RJ45网口的参考连接电路有哪些,主要注意事项在哪里呢?

Hqst华轩盛(石门盈盛)电子导读&#xff1a;一起来了解常用的百兆网络变压器与RJ45网口的参考连接电路有哪些&#xff0c;主要注意事项在哪里呢&#xff1f; 第一,常用的百兆网络变压器与RJ45网口的参考连接电路 常用百兆网络变压器与网口连接器分开为独立电子元件的分离式参考电…

TrustZone之与非安全虚拟化交互

到目前为止&#xff0c;我们在示例中忽略了非安全状态中可能存在的虚拟化程序。当存在虚拟化程序时&#xff0c;虚拟机与安全状态之间的许多通信将通过虚拟化程序进行。 例如&#xff0c;在虚拟化环境中&#xff0c;SMC用于访问固件功能和可信服务。固件功能包括诸如电源管理之…

Ubuntu 常用命令之 top 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 top命令是Linux下常用的性能分析工具&#xff0c;可以实时动态地查看系统中各个进程的资源占用状况&#xff0c;类似于Windows的任务管理器。它可以显示系统总的和分区的CPU使用率、内存使用率、交换区使用率、系统负载、进程数、…

MFC静态链接+libtiff静态链接提示LNK2005和LNK4098

编译报错 1>msvcrt.lib(ti_inst.obj) : error LNK2005: "private: __thiscall type_info::type_info(class type_info const &)" (??0type_infoAAEABV0Z) 已经在 libcmtd.lib(typinfo.obj) 中定义 1>msvcrt.lib(ti_inst.obj) : error LNK2005: "pr…

BUUCTF-Linux Labs

Linux Labs 根据题目给出的内容&#xff0c;在kali中连接靶机&#xff0c;输入密码进入命令行模式 ls发现什么都没有&#xff0c;有可能进入到了一个空文件夹 cd .. 切换到上一层目录&#xff0c;ls查看此目录下的内容&#xff0c;发现flag.txt文件&#xff0c;查看文件是flag …

超维空间S2无人机使用说明书——32、使用yolov7进行目标识别

引言&#xff1a;为了提高yolo识别的质量&#xff0c;提高了yolo的版本&#xff0c;改用yolov7进行物体识别&#xff0c;同时系统兼容了低版本的yolo&#xff0c;包括基于C的yolov3和yolov4&#xff0c;也有更高版本的yolov8。 简介&#xff0c;为了提高识别速度&#xff0c;系…

w15初识php基础

一、计算100之内的偶数之和 实现思路 所有的偶数除2都为0 代码实现 <?php # 记录100以内的偶数和 $number1; $num0; while($number<100){if($number%20){ $num$number;}$number1; } echo $num; ?>输出的结果 二、计算100之内的奇数之和 实现思路 所有的奇数除…

HTML+CSS做一个时尚柿子造型计时器

文章目录 💕效果展示💕代码展示HTMLJS💕效果展示 💕代码展示 HTML <!DOCTYPE html> <html lang

使用低代码工具构建电商平台:简化开发流程,加速应用搭建

在数字化时代&#xff0c;电商平台成为了各类企业的重要组成部分。然而&#xff0c;传统的软件开发过程往往漫长而复杂&#xff0c;需要大量的编码和调试工作。随着低代码工具的出现&#xff0c;开发者可以通过简化的方式来搭建电商平台应用&#xff0c;从而更快速地满足业务需…

surface pro重置win10后没微软账号如何登录

Surface pro官方镜像恢复详细图文步骤-CSDN博客 如果不懂重置系统请看上面的链接 win10-win11都开始默认要求输入微软账号才能使用。但依然有入口可以给没微软账号的用户使用的 第一步&#xff0c;重置后会要求连互联网&#xff0c;这一步要自己找个网络连接上&#xff0c;不…

十大经典排序算法(个人总结C语言版)

文章目录 一、前言二、对比1.排序算法相关概念1.1 时间复杂度1.2 空间复杂度1.3 排序方式1.4 稳定度 2.表格比较3.算法推荐3.1 小规模数据3.2 中等规模数据3.3 大规模数据3.4 特殊需求 三、排序算法1.冒泡排序&#xff08;Bubble Sort&#xff09;1.1 简介1.2 示例代码&#xf…

智能酒精壁炉在户外装饰中展现的独特魅力

智能酒精壁炉&#xff0c;一种独特的户外装饰和实用性产品&#xff0c;以其独有的魅力在户外场景中迅速赢得了人们的喜爱和欢迎。 在露营时&#xff0c;智能酒精壁炉由于便携性&#xff0c;可以很轻松地携带到露营地点。并且无需外接电源或气体&#xff0c;为露营者提供了简单而…

swing快速入门(二十五)

注释很详细&#xff0c;直接上代码 新增内容 1.ImageIO.write读取并显示图片 2.ImageIO.writeImageIO.write读取并保存图片 package swing21_30;import javax.imageio.ImageIO; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent…