二叉树进阶-二叉搜索树

目录

1.二叉树的概念

2.二叉搜索树的操作

2.1二叉搜索树的结构

2.2实现节点的查找(find)

2.3实现增加节点(insert)

2.4实现删除节点(erase)

2.5析构函数

2.6二叉搜索树的完整实现

3.二叉搜索树的应用

3.1 K模型

3.2KV模型

4.二叉搜索树的性能分析


1.二叉树的概念

二叉搜索树也叫二叉排序数(因为按照中序遍历是有序的),对于非空二叉树满足以下性质

a.非空左子树的所有键值(key)都小于根节点的键值;b.非空右子树的所有健值都大于根节点的键值c.根节点的左右子树都是二叉搜索树。如图就是一颗二叉搜索树

2.二叉搜索树的操作

2.1二叉搜索树的结构

a.构建二叉搜索树需要定义一个struct BSTNode 节点信息,节点中包括val值,右子树节点地址,左子树节点地址。并完成构造函数b.构建二叉搜索树,还需要树本体,里面存储root根节点信息,并包装各种接口实现对其的增删查改管理。

template<class K>
struct BSTNode
{BSTNode(const K& key):_key(key):_left(nullptr):_right(nullptr){}K _key;BSTNode<K>* _left;BSTNode<K>* _right;
};template<class K>
class BSTree
{typedef BSTNode<K> Node;
public://接口实现
private:Node* _root=nullptr;
};

2.2实现节点的查找(find)

find实现可分为以下步骤:①比我小,那么迭代到左节点寻找,如果比我大,那么迭代到右节点寻找。②如果在cur为空前,都没找到,那么就没有此节点。

bool find(const K& key)
{Node* cur = _root;while (cur){if (key > cur->_key) cur = cur->right;else if (key < cur->_key) cur = cur->left;else return false;}return true;
}

2.3实现增加节点(insert)

insert的实现可分为以下步骤:①如果二叉树为空(头节点为空),直接将new的新节点赋值给_root即可②如果为非空树,那么先find找到位置(如果存在),用双指针法找,当前驱指针为空时,后继节就是我们要插入节点的父节点。

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

2.4实现删除节点(erase)

erase的实现比较复杂,需要考虑一下几点:通过find找到要删的节点(如果有),①如果要删除的节点左子树为空树,那么将其右树的节点代替其位置与父建立连接。②如果要删除的节点右树为空树,那么将其左树代替其位置与父建立连接。③如果左右都无子树,且有父节点,那么可复用上述①②。④如果左无子树,且无父节点,那么直接将_root置为其右树节点,同样如果无右子树,且无父节点,那么直接将_root置为其左子树节点,如果左右无子树,且无父节点,可复用。⑤如果左树与右数都不为空,那么用将其置换为左树的最右节点或者右树的最左节点,然后删除被置换的节点,注意要继承被删除节点的左子树或者右子树⑥注意在完成第⑤步骤时,可能左树的最右节点就是左树的跟(因为左树没有右子树),同理,可能右树的最左节点就是右树的跟(因为右树没有左子树)。

bool erase(const K& key)
{Node* cur = _root;Node* prev = nullptr;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else //找到了要删除的erase节点,此时cur是我们要删除的节点,prev是其父节点{if (cur->_left == nullptr){if (prev == nullptr){_root = cur->_right;delete cur;}else {if (prev->_left == cur){prev->_left = cur->_right;}else if (prev->_right == cur){prev->_right = cur->_right;}delete cur;}}else if (cur->_right == nullptr){if (prev == nullptr){_root = cur->_left;delete cur;}else{if (prev->_left == cur){prev->_left = cur->_left;}else if (prev->_right == cur){prev->_right = cur->_left;}delete cur;}}else if (cur->_left != nullptr && cur->_right != nullptr){Node* right2left = cur->_right;Node* newprev = cur;while (right2left->_left){newprev = right2left;right2left = right2left->_left;}swap(cur->_key, right2left->_key);if (newprev->_left == right2left){newprev->_left = right2left->_right;} else if (newprev->_right == right2left){newprev->_right = right2left->_right;}delete right2left;}return true;}}return false;
}

2.5析构函数

二叉树的析构函数,可以利用函数递归到底层,再逐步删除节点利用后序遍历可以保证跟节点最后delete。

~BSTree() // 析构函数,利用函数递归进行析构
{destroy_bst(_root);
}
void destroy_bst(Node* root)
{if (root == nullptr) return;destroy_bst(root->_left);destroy_bst(root->_right);delete root;
}

2.6二叉搜索树的完整实现

#include<iostream>
using namespace std;template<class K>
struct BSTNode
{BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}K _key;BSTNode<K>* _left;BSTNode<K>* _right;
};template<class K>
class BSTree
{typedef BSTNode<K> Node;
public://接口实现~BSTree() // 析构函数,利用函数递归进行析构{destroy_bst(_root);}void destroy_bst(Node* root){if (root == nullptr) return;destroy_bst(root->_left);destroy_bst(root->_right);delete root;}bool find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key) cur = cur->right;else if (key < cur->_key) cur = cur->left;else return false;}return true;}bool insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* prev = nullptr;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else{return false;}}if (key > prev->_key){prev->_right = new Node(key);}else{prev->_left = new Node(key);}return true;}bool erase(const K& key){Node* cur = _root;Node* prev = nullptr;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else //找到了要删除的erase节点,此时cur是我们要删除的节点,prev是其父节点{if (cur->_left == nullptr){if (prev == nullptr){_root = cur->_right;delete cur;}else {if (prev->_left == cur){prev->_left = cur->_right;}else if (prev->_right == cur){prev->_right = cur->_right;}delete cur;}}else if (cur->_right == nullptr){if (prev == nullptr){_root = cur->_left;delete cur;}else{if (prev->_left == cur){prev->_left = cur->_left;}else if (prev->_right == cur){prev->_right = cur->_left;}delete cur;}}else if (cur->_left != nullptr && cur->_right != nullptr){Node* right2left = cur->_right;Node* newprev = cur;while (right2left->_left){newprev = right2left;right2left = right2left->_left;}swap(cur->_key, right2left->_key);if (newprev->_left == right2left){newprev->_left = right2left->_right;} else if (newprev->_right == right2left){newprev->_right = right2left->_right;}delete right2left;}return true;}}return false;}void _InOrder(Node* root){if (root == nullptr) return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}void InOrder(){_InOrder(_root);cout << endl;}
private:Node* _root=nullptr;
};

3.二叉搜索树的应用

3.1 K模型

即二叉搜索树的节点中只存储key一个关键码。代码就是我们上述实现的普通二叉树。

它应用在查找一个key在不在节点中,例如门禁系统:我们或是通过人脸识别,或是通过RFID射频识别技术获得一个关键码key,如果find找到在BSTree中,那么就识别成功,否则识别失败。他的速度非常快,O(log N),意味着13亿人,最多只需要31次就能找到具体的一个人。

3.2KV模型

即二叉搜索树的节点中除了key,还存储了val,通过key可以通过find映射到val。它的代码实现如下,与K模型非常相似。(它的find,erase和insert都是通过key实现的)

template<class K,class V>
struct BSTNode  //binary search node
{BSTNode(const K& key,const V& val ):_key(key),_left(nullptr),_right(nullptr),_val(val){}BSTNode<K,V>* _left;BSTNode<K,V>* _right;K _key;V _val;};
template<class K,class V>
class BSTree
{typedef  BSTNode<K,V> Node;
public:bool insert(const K& key,const V& val){if (_root == nullptr){_root = new Node(key,val);}Node* cur = _root;Node* prev = nullptr;while (cur){if (cur->_key > key){prev = cur;cur = cur->_left;}else if (cur->_key < key){prev = cur;cur = cur->_right;}else{return false;}}if (key < prev->_key)prev->_left = new Node(key,val);elseprev->_right = new Node(key,val);return true;}void _InOrder(Node* root) //中序遍历{if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << "->"<<root->_val<<" ";_InOrder(root->_right);}void InOrder(){_InOrder(_root);}Node* find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key)cur = cur->_right;else if (key < cur->_key)cur = cur->_left;elsereturn cur;}return nullptr;}bool erase(const K& key){Node* prev = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else{if (cur->_left == nullptr ){//如果要删的节点是根节点if (cur == _root){_root = cur->_right;delete cur;}else{if (prev->_left == cur)prev->_left = cur->_right;else if (prev->_right == cur)prev->_right = cur->_right;delete cur;}}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;delete cur;}else{if (prev->_left == cur)prev->_left = cur->_left;else if (prev->_right == cur)prev->_right = cur->_left;delete cur;cur = nullptr;}}else {//需要考虑特殊情况,当leftmax的右为空时Node* prev_leftmax = cur;Node* leftmax = cur->_left;while (leftmax->_right){prev_leftmax = leftmax;leftmax = leftmax->_right;}cur->_key = leftmax->_key;if (prev_leftmax->_left == leftmax)prev_leftmax->_left = leftmax->_left;elseprev_leftmax->_right = leftmax->_left;delete leftmax;leftmax = nullptr;}return true;}}if (cur == nullptr)return false;}
public:Node* _root = nullptr;
};

4.二叉搜索树的性能分析

值得注意的是,一组数据,通过不同顺序输入所构建的二叉搜索树不同。

如果构建的是一颗完全二叉树,或者接近完全二叉树,那么其查找的效率就很高,O(logN);

如果输入的数据是有序的,那么构建的二叉树就会退化为单支二叉树,那么效率就会降低,接近O(N)

后续我们学习了AVL树和红黑树就可以解决这个问题,实现一颗左右接近平衡的二叉树

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

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

相关文章

「Mac畅玩鸿蒙与硬件24」UI互动应用篇1 - 灯光控制小项目

本篇将带领你实现一个互动性十足的灯光控制小项目&#xff0c;用户可以通过点击按钮来控制灯光的开关。该项目将涉及状态管理、动态图片加载以及按钮交互&#xff0c;是学习鸿蒙应用开发的重要基础。 关键词 UI互动应用状态管理动态图片加载用户交互 一、功能说明 在这个灯光…

自制inscode项目推荐:色块小游戏

小编的inscode部署项目&#xff1a;割绳子游戏。 更多精彩内容见InsCode - 让你的灵感立刻落地~ 介绍一下项目及玩法。 游戏概述 颜色匹配小游戏是一款基于HTML、CSS和JavaScript开发的简单而有趣的网页游戏。游戏的目标是通过点击颜色块&#xff0c;将整个游戏板上的所有方块…

DevOps赋能:优化业务价值流的实战策略与路径(下)

下篇&#xff1a;加速工作项流动与持续改进优化 —— 跨越差距&#xff0c;迈向卓越交付 在上篇中&#xff0c;我们已经深入探讨了看板方法的四大核心实践&#xff0c;它们共同致力于实现“顺畅且高质量地交付价值”的终极目标。然而&#xff0c;理想与现实之间往往存在一定的…

使用带有令牌认证的 Jupyter Notebook 服务器

当你不想在默认浏览器打开Jupyter Notebook,但是在其他浏览器打开http://localhost:8890/lab或者http://localhost:8889/tree&#xff0c;却显示 Token authentication is enabled&#xff0c;如下图 可以按以下步骤操作&#xff1a; 获取令牌&#xff1a;在启动 Jupyter Note…

FRIENDLYARM Tiny6410 superboot烧写进sd卡教程

友善之臂真的垃圾&#xff0c;pdf乱不说&#xff0c;资料在哪也不说&#xff0c;没有视频教程&#xff0c;就染你自己去一堆资料里找&#xff0c;** superboot在资料B盘tooles-image里 注意有些朋友scan不到sd卡是因为没有给这个软件开启管理员模式&#xff0c;他没权限去扫描…

宝藏虚拟化学习资料大全

最近发现了关于虚拟化的宝藏资料&#xff0c;瑞斯拜&#xff01;原文链接如下&#xff1a; 500篇关于虚拟化的经典资料&#xff0c;含CPU虚拟化&#xff0c;磁盘虚拟化&#xff0c;内存虚拟化&#xff0c;IO虚拟化。 目录 &#x1fa90; 虚拟化基础 &#x1f343; 虚拟化分类&…

C++模拟真人动态生成鼠标滑动路径

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

【Redis实践】使用zset实现实时排行榜以及一些优化思考

文章目录 1.概述2.zset的基本概念说明2.1.数据结构说明2.2.zset做排行榜的指令 3. 项目中的实践3.1.RedisTemplate实现排行榜3.2.可能存在的问题及解决方案3.2.1. 限制成员的数量3.2.2.保留当前分数与最高分数3.2.3.批量操作成员分数&#xff0c;减少并发 4.总结 1.概述 我们在…

C++_STL_xx_番外01_关于STL的总结(常见容器的总结;关联式容器分类及特点;二叉树、二叉搜索树、AVL树(平衡二叉搜索树)、B树、红黑树)

文章目录 1. 常用容器总结2. 关联式容器分类3. 二叉树、二叉搜索树、AVL树、B树、红黑树 1. 常用容器总结 针对常用容器的一些总结&#xff1a; 2. 关联式容器分类 关联式容器分为两大类&#xff1a; 基于红黑树的set和map&#xff1b;基于hash表的unorder_set和unorder_ma…

【LwIP源码学习4】主线程tcpip_thread

前言 本文对lwip的主要线程tcpip_thread进行分析。 正文 tcpip_thread是lwip最主要的线程&#xff0c;其创建在tcpip_init函数中 sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);tcpip_init函数被TCPIP_Init函数调用。…

光圈,感光度,感光器件

光圈&#xff08;通光孔&#xff09;&#xff0c;是一个用来控制光线透过镜头进入机身内感光面光量的装置&#xff0c;通常设置在镜头内。通常&#xff0c;我们用f值来表达光圈大小。通俗来说&#xff0c;摄像机镜头拍照时&#xff0c;不可能随意改变镜头直径&#xff0c;但可以…

Llama 3.2 Vision Molmo:多模态开源生态系统基础

编者按&#xff1a; 视觉功能的融入对模型能力和推理方式的影响如何&#xff1f;当我们需要一个既能看懂图像、又能生成文本的 AI 助手时&#xff0c;是否只能依赖于 GPT-4V 这样的闭源解决方案&#xff1f; 我们今天为大家分享的这篇文章&#xff0c;作者的核心观点是&#xf…

高效视频制作大提速,视频剪辑软件的高级自定义命令功能批量调整视频的色调、饱和度和亮度,轻松驾驭视频编辑技巧

在浩瀚的数字海洋中&#xff0c;视频如同璀璨的星辰&#xff0c;而每一颗星辰都渴望被精心雕琢&#xff0c;闪耀出最独特的光芒。想象一下&#xff0c;你手握一把神奇的钥匙&#xff0c;能够轻松解锁批量视频剪辑的奥秘&#xff0c;让每一帧画面都跃动着你的创意与激情。这把钥…

[RootersCTF2019]ImgXweb

审题 看到robots.txt,看到里面的文件&#xff0c;打开看到 you-will-never-guess这个字符串 进行注册登录 可以看到典型的jwt加密的Cookie 想到之前的字符串可能是密匙&#xff0c;更改为admin&#xff0c;进行登录。 成功后可以看到flag.png。 发现图形打不开 使用curl进…

32单片机HAL库的引脚初始化

在使用HAL库时&#xff0c;GPIO初始化函数定义在stm32f4xx_hal_gpio.c文件中&#xff0c;如下&#xff1a; void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); 由这个函数可以看出&#xff0c;在初始化GPIO时&#xff0c;需要向函数传入2个结构体&…

ubuntu【桌面】 配置NAT模式固定IP

DHCP分配导致虚拟机IP老变&#xff0c;SSH老要重新配置&#xff0c;设成静态方便些 一、设NAT模式 1、设为NAT模式 2、看模式对应的虚拟网卡 - VMnet8 3、共享主机网卡网络到虚拟网卡 - VMnet8 二、为虚拟网卡设置静态IP 记住这个IP 三、设置ubuntu固定IP 1、关闭DHCP并…

确保企业架构与业务的一致性与合规性:数字化转型中的关键要素与战略实施

在现代企业的数字化转型过程中&#xff0c;确保企业架构&#xff08;Enterprise Architecture, EA&#xff09;与企业业务的紧密一致性与合规性至关重要。无论是在战略层面还是运营层面&#xff0c;EA都为企业的未来发展提供了清晰的蓝图&#xff0c;确保企业在应对复杂的业务环…

Pinctrl子需要中client端使用pinctrl过程的驱动分析

往期内容 本专栏往期内容&#xff1a; Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析&#xff1a;imx_pinctrl_soc_info结构体 input子系统专栏…

Failed to search for file: Cannot update read-only repo

今天在读《Linux就该这么学》并上机操作RedHat Linux 8。结果在执行指令时却出现了问题: 我明明已经是root权限了&#xff0c;我于是上网去找&#xff0c;但也没看到合适的解答。为什么会和书上的操作结果不一样。 后来我突然意识到是不是我打了不该打的空格&#xff0c;于是…

SpringBoot实现验证码案例

目录 实现逻辑前后端交互接口前端代码后端代码 实现逻辑 1、后端功能&#xff1a;随机生成验证码图片&#xff0c;并把交给前端、接收用户输入&#xff0c;验证用户输入的验证码是否正确、 2、前端功能&#xff1a;显示验证码&#xff0c;提供输入框供用户输入他们看到的验证…