二叉搜索树详解

一、二叉搜索树的概念

二叉搜索树又名二叉排序树以及二叉查找树,它是一颗空树或者是具有以下性质的二叉树

*若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

*若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

*它的左右子树分别为二叉搜索树。

二、基本操作

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

1、二叉树的查找

a、从根节点开始查找,比根大的值往右边走查找,比根值小则往左边查找。

b、最多查找高度次,走到空节点,还没找到则这个值不存在。

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

2、二叉搜索树的插入

插入的具体过程为

a、树为空,则直接新增新节点,赋值给root指针

b、如果树不为空。按照二叉搜索树的查找方式找到插入位置,插入新节点。

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->_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;
}

3、删除元素

删除元素是二叉搜索树实现中一个比较难的点,需要考虑多种情况。

1、首先要查找元素是否在二叉搜索树中,如果不存在则返回false,如果存在,则删除所在节点位置,并可能对其他节点进行调整。有以下四种情况。

a、要删除的节点无孩子节点

b、要删除的节点只有左孩子节点

c、要删除的节点只有右孩子节点

d、要删除的节点即有左孩子节点又有右孩子节点

情况a可以与情况b和情况c进行合并。

情况b、删除该节点并使该节点的父亲指针被删除指针指向被删除节点的右孩子指针。

情况c、删除该节点并使该节点的父亲指针被删除指针指向被删除节点的左孩子指针。

情况d、在它的右子树中寻找中序下的第一个节点,用它 的值填补到被删除节点中,再去处理该节点的删除问题----替换删除

bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除// 左为空,父亲指向我的右if (cur->_left == nullptr)//第一种情况||{//if(parent == nullptr)if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr)//第二种情况{//if(parent == nullptr)if (cur == _root){_root = cur->_left;}else{// 右为空,父亲指向我的左if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else//第三种情况{// 左右都不为空,替换法删除// // 查找右子树的最左节点替代删除Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}swap(cur->_key, rightMin->_key);if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}return true;}}return false;
}

4、性能分析

由上述可知,删除和插入操作都必须先查找,查找效率决定了二叉搜索树各种功能的操作的性能。

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

在最理想的情况下,即二叉树为完全二叉树,其平均比较次数为0(logn);

最坏情况发下,即二叉树为单叉树,其平均比较次数为o(n);这个时间复杂度是十分可怕的!!!

所有在“大佬“的改进下,我们有引入的平衡二叉树和红黑树的概念,使得二叉树的性能有了较大的提升,下一篇内容将会给大家介绍平衡二叉数。

三、二叉树的应用

二叉树有多种应用场景,有k模型以及kv模型

1、K模型:只有Key作为关键值,节点中只存储key即可,关键值为需要搜索到该节点的值。

比如给定一个单词word,搜索其拼写是否正确,我们只需要得到一个由所有单词作为键值的二叉搜索树,在二叉搜索树中检索其是否存在即可,不存在即为拼写错误。

2、KV模型:每一个关键值,都有一个唯一的value值与之对应。即<Key,Value>的键值对。

比如:1、英汉词典就是英文和中文的对应关系,通过英文可以快速找到其对应的中文,。

2、统计单词的次数,给定一个单词的拼写,就可以快速找到其出现的次数单词由其出现次数就是<Word,Count>的一种键值对。

以下附二叉搜索树底层实现的源代码。!~!!

#pragma once
#include<string>namespace key
{template<class K>//struct BinarySearchTreeNodestruct 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: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->_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){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}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->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除// 左为空,父亲指向我的右if (cur->_left == nullptr)//第一种情况||{//if(parent == nullptr)if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr)//第二种情况{//if(parent == nullptr)if (cur == _root){_root = cur->_left;}else{// 右为空,父亲指向我的左if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else//第三种情况{// 左右都不为空,替换法删除// // 查找右子树的最左节点替代删除Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}swap(cur->_key, rightMin->_key);if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}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 << " ";_InOrder(root->_right);}private:Node* _root = nullptr;};void TestBSTree1(){int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> t1;for (auto e : a){t1.Insert(e);}t1.InOrder();//t1.Erase(3);t1.Erase(8);t1.InOrder();for (auto e : a){t1.Erase(e);t1.InOrder();}}
}namespace key_value
{template<class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _value;// pair<K, V> _kv;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:// logNbool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}Node* parent = nullptr;Node* cur = _root;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, value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return cur;}}return cur;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除// 左为空,父亲指向我的右if (cur->_left == nullptr){//if(parent == nullptr)if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){//if(parent == nullptr)if (cur == _root){_root = cur->_left;}else{// 右为空,父亲指向我的左if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 左右都不为空,替换法删除// // 查找右子树的最左节点替代删除Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}swap(cur->_key, rightMin->_key);if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}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;};// 20:18// 查找// 二分查找// 二叉搜索树查找 -> AVL树和红黑树// 哈希查找// 跳表查找// 多叉搜索树查找 B树系列void TestBSTree2(){BSTree<string, string> dict;dict.Insert("string", "字符串");dict.Insert("left", "左边");dict.Insert("insert", "插入");//...string str;while (cin >> str){BSTreeNode<string, string>* ret = dict.Find(str);if (ret){cout << ret->_value << endl;}else{cout << "无此单词,请重新输入" << endl;}}}void TestBSTree3(){// 统计次数string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" };BSTree<string, int> countTree;for (const auto& str : arr){auto ret = countTree.Find(str);if (ret == nullptr){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();}
}

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

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

相关文章

昂科烧录器支持KIOXIA铠侠的可编程只读存储器TH58NVG4S0HTAK0

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中KIOXIA铠侠的电可擦除可编程只读存储器TH58NVG4S0HTAK0已经被昂科的通用烧录平台AP8000所支持。 TH58NVG4S0HTAK0是一个单一的3.3V 16Gbit&#xff08;18253611008位&#xff…

QT拖放事件之一:初识拖放4大事件处理函数

0、拖放 两个动作,合在一起称之为拖放事件; 拖:就是拖着走; 放:就是拖着走,然后松开鼠标了,释放了,这就是放; 注意:放:拖着的东西要放在什么地方??? 假如,我将一个记事本拖着跑,然后放到一个Widget窗口上,那么为了使得Widget能感知相应的事件(拖着进入事件…

智慧城市低空+AI视频智能监控:构建新时代安全防线

随着科技的飞速发展&#xff0c;智能监控技术已经广泛应用于各个领域&#xff0c;从城市治理到工业生产&#xff0c;从公共安全到环境监测&#xff0c;都发挥着越来越重要的作用。而在低空领域&#xff0c;AI视频智能监控方案的建设更是成为了一个热点话题。 一、低空AI视频智…

设计模式原则——迪米特法则原则

设计模式原则 设计模式示例代码库地址&#xff1a; https://gitee.com/Jasonpupil/designPatterns 迪米特法则原则&#xff1a; 意义在于降低类之间的耦合。由于每个对象尽量减少对于其他对象的了解&#xff0c;因此&#xff0c;很容易使得系统的功能模块功能独立&#xff…

[论文笔记]Mixture-of-Agents Enhances Large Language Model Capabilities

引言 今天带来一篇多智能体的论文笔记&#xff0c;Mixture-of-Agents Enhances Large Language Model Capabilities。 随着LLMs数量的增加&#xff0c;如何利用多个LLMs的集体专业知识是一个令人兴奋的开放方向。为了实现这个目标&#xff0c;作者提出了一种新的方法&#xf…

Erpnext安装

Erpnext安装 环境要求 Ubuntu 23.04 x86_64 Python 3.10.12 pip 23.0.1 node v18.16.0 npm 9.5.1 yarn 1.22.22 MariaDB 10.11.2 Redis 7.0.8 wkhtmltox 0.12.6.1 bench 5.22.6环境安装 Reids 安装 // 安装7.0.8 也可不指定版本 直接执行 sudo apt install redis-server s…

Spring Boot 3 搭建

1、jdk 17 2、spring boot 3.1.7 3、pom.xml <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xs…

在线客服源码系统全端通用 源码完全开源可以二次开发 带完整的安装代码包以及搭建教程

系统概述 在线客服源码系统采用了先进的技术架构&#xff0c;包括前端界面、后端服务、数据库等部分。前端界面采用了响应式设计&#xff0c;能够自适应不同的设备屏幕尺寸&#xff0c;为用户提供良好的使用体验。后端服务采用了高性能的服务器架构&#xff0c;确保系统的稳定…

【面向对象】复习(三)

this指针 本职&#xff1a;指针常量&#xff0c;指向当前对象 不能访问静态成员&#xff0c;因为静态成员属于类&#xff0c;this指针属于对象 不要返回局部变量的引用或指针 int f(){ int a1; return a;}//a是函数外的&#xff0c;函数结束不释放 A * f(A & a){return…

QT学习积累——在C++中,for循环中使用``与不使用``的区别和联系

目录 引出使用&与不使用&除法的一个坑 总结自定义信号和槽1.自定义信号2.自定义槽3.建立连接4.进行触发 自定义信号重载带参数的按钮触发信号触发信号拓展 lambda表达式返回值mutable修饰案例 引出 QT学习积累——在C中&#xff0c;for循环中使用&与不使用&的…

即时发布,市场担忧导致比特币和以太坊全线暴跌

来源:币界网 作者:636Marx 币界网新闻 – 2024 年 6 月 25 日– 数字货币市场形势急转直下&#xff0c;两种主力的数字货币比特币和以太坊的价格大幅下跌&#xff0c;给数字货币市场带来冲击。比特币再次触及$60,000美元关口&#xff0c;而以太坊反弹乏力&#xff0c;在 3,300…

PointCloudLib (多线程)快速双边滤波 C++版本

0.实现效果 原始点云 和滤波后的点云对比 1.算法原理 PCL(Point Cloud Library)快速双边滤波是一种高效的点云数据滤波方法,它基于传统双边滤波算法进行了改进,通过引入近似方法加速计算过程。以下是关于PCL快速双边滤波的详细回答: 1. 基本原理 空间滤波:在点云中,相…

详解 ClickHouse 的 MaterializeMySQL 引擎

注意与 ClickHouse 的 MySQL 表引擎区分开 一、概述 ClickHouse 20.8.2.3 版本新增加了 MaterializeMySQL 的 database 引擎&#xff0c;该 database 能映射到 MySQL 中的某个 database &#xff0c; 并自动在 ClickHouse 中创建对应的 ReplacingMergeTree。ClickHouse 服务做…

Verilog的逻辑系统及数据类型(一):四值逻辑系统

目录 1. Verilog采用的四值逻辑系统2.主要数据类型2.1 net&#xff08;线网&#xff09;2.2 寄存器类 &#xff08;register)2.3 Verilog中net和register声明语法2.3.1 net声明2.3.2 寄存器声明 2.4 选择正确的数据类型2.5 选择数据类型时常犯的错误2.5.1 信号类型确定方法总结…

【嵌入式DIY实例】-Nokia 5110显示BME280传感器数据

Nokia 5110显示BME280传感器数据 文章目录 Nokia 5110显示BME280传感器数据1、硬件准备与接线2、代码实现本文将介绍如何使用 ESP8266 NodeMCU 板(ESP12-E 模块)和 BME280 气压、温度和湿度传感器构建一个简单的本地气象站。 NodeMCU 从 BME280 传感器读取温度、湿度和压力值…

2024广东省职业技能大赛云计算赛项实战——集群部署GitLab Runner

集群部署GitLab Runner 前言 题目如下: 部署GitLab Runner 将GitLab Runner部署到gitlab-ci命名空间下&#xff0c;Release名称为gitlab-runner&#xff0c;为GitLab Runner创建持久化构建缓存目录/home/gitlab-runner/ci-build-cache以加速构建速度&#xff0c;并将其注册到…

【算法与数据结构】【字符串篇】【String的常见函数】

系列文章 本人系列文章-CSDN博客https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5502 1.string基本概念 string是C风格的字符串&#xff0c;而string本质上是一个类。 string和char * 区别&#xff1a; char * 是一个指针 string是一…

Linux操作系统:从入门到精通

前言 Linux操作系统是当今计算机世界中的重要一环。它不仅在服务器和企业级应用中广泛使用&#xff0c;同时也是许多开发者和技术爱好者的首选。本文将带你全面了解Linux操作系统的基础知识、常用命令及其在不同领域的应用。 一、Linux简介 1.1 什么是Linux&#xff1f; Li…

Java常用类2

StringBuffer和StringBuilder StringBuffer与StringBuilder类介绍 StringBuffer是String的对等类&#xff0c;提供了许多字符串功能。您可能知道&#xff0c;String表示长度固定、不可修改的字符序列。与之相对应&#xff0c;StringBuffer表示可增长、可写入的字符序列。Stri…

重写equals为什么要重写hashCode???

当你在Java中重写了equals()方法后&#xff0c;通常建议你也应该重写hashCode()方法。这是基于Java集合框架的设计原则&#xff0c;具体来说是基于Object类中的equals()和hashCode()方法之间的约定。以下是为什么需要这样做的一些关键原因&#xff1a; 一致性原则&#xff1a;…