【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)



快乐的流畅:个人主页


个人专栏:《C语言》《数据结构世界》《进击的C++》

远方有一堆篝火,在为久候之人燃烧!

文章目录

  • 引言
  • 一、二叉搜索树介绍
  • 二、二叉搜索树的模拟实现
    • 2.1 结点
    • 2.2 成员变量
    • 2.3 默认成员函数
      • 2.3.1 constructor
      • 2.3.2 copy constructor
      • 2.3.3 operator=
      • 2.3.4 destructor
    • 2.4 中序遍历
    • 2.5 查找
      • 2.5.1 迭代实现
      • 2.5.2 递归实现
    • 2.6 插入
      • 2.6.1 迭代实现
      • 2.6.2 递归实现
    • 2.7 删除
      • 2.7.1 迭代实现
      • 2.7.2 递归实现
  • 三、二叉搜索树的应用
  • 四、二叉树进阶面试题

引言

二叉树在之前的数据结构章节讲解过,当时使用C来实现。而如今学习的二叉搜索树,便是二叉树的进阶,也更适合使用C++来实现。

一、二叉搜索树介绍

二叉搜索树(BST,Binary Search Tree),又称为二叉排序树。

它满足以下性质:

  • 非空左子树的所有键值小于其根结点的键值
  • 非空右子树的所有键值大于其根结点的键值
  • 左右子树均为二叉搜索树

二、二叉搜索树的模拟实现

2.1 结点

template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key): _left(nullptr), _right(nullptr), _key(key){}
};

细节:在二叉搜索树中,一般更喜欢用K作为模板参数,用key作为数据,称为键值。

2.2 成员变量

template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
protected:Node* _root = nullptr;
};

2.3 默认成员函数

2.3.1 constructor

//写法一
BSTree(): _root(nullptr)
{}
//写法二
BSTree() = default;//强制生产默认构造

2.3.2 copy constructor

BSTree(const BSTree<K>& t)
{_root = Copy(t._root);
}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;
}

细节:写一个子函数Copy,进行前序遍历递归拷贝

2.3.3 operator=

BSTree<K>& operator=(BSTree<K> t)
{swap(_root, t._root);return *this;
}

细节:现代写法,直接拷贝完交换

2.3.4 destructor

~BSTree()
{Destroy(_root);
}void Destroy(Node*& root)
{if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;
}

细节:

  1. 写一个子函数Destroy,进行后序遍历递归释放
  2. 参数为Node*&,这样就可以在函数内置空根节点

2.4 中序遍历

为什么只介绍中序遍历呢?二叉搜索树,之所以又称为二叉排序树,是因为在中序遍历时,便能将数据按升序遍历

void InOrder()
{_InOrder(_root);cout << endl;
}void _InOrder(Node* root)
{if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);
}

细节:同样,一般要递归写一个子函数,再进行传参控制

2.5 查找

二叉搜索树,其最大优势肯定是在于搜索!

2.5.1 迭代实现

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

细节:

  1. 查找的key比当前结点的_key大,则往右查找
  2. 查找的key比当前结点的_key小,则往左查找
  3. 找到返回true,走到空找不到返回false

2.5.2 递归实现

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

2.6 插入

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

细节:

  1. 若根为空,直接创建结点,返回true
  2. 设置parent变量,记录父节点,以便在cur走到空时进行插入
  3. 插入前,要判断用左指针还是右指针链接

2.6.2 递归实现

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

细节:参数为Node*&,使得当前root为空,却可以直接创建节点链接(因为此时root是其父节点左右指针的引用)

2.7 删除

2.7.1 迭代实现

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->_right == nullptr)//右子树为空{if (cur == _root){_root = _root->_left;}else{if (parent->_right == cur){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;}else if (cur->_left == nullptr)//左子树为空{if (cur == _root){_root = _root->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else//左右子树均不为空{//这里选择找右子树的最左节点Node* pminRight = cur;Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (pminRight->_right == minRight){pminRight->_right = minRight->_right;}else{pminRight->_left = minRight->_right;}delete minRight;}return true;}}return false;
}

细节:

  1. 首先依然要parent记录父节点,进行查找
  2. 找到了,分三种删除情况:
    • 右子树为空
    • 左子树为空
    • 左右子树均不为空
  3. 左右子树一边为空时,先判断parent是否为空,如果为空,代表cur为_root,需要移动_root;如果不为空,则再判断左右指针链接
  4. 左右子树均不为空时,寻找右子树的最左结点minRight(也可以是左子树的最右结点)来替代cur,注意minRight可能有孩子,还要设置pminRight记录其父节点位置,判断左右指针链接

2.7.2 递归实现

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->_right == nullptr)//右子树为空{root = root->_left;}else if (root->_left == nullptr)//左子树为空{root = root->_right;}else{Node* minRight = root->_right;while (minRight->_left){minRight = minRight->_left;}swap(root->_key, minRight->_key);return _EraseR(root->_right, key);}delete del;return true;}
}

细节:

  1. 参数为Node*&,使得左右子树一边为空时,不用判断,直接链接(将两种情况一并处理了)
  2. 左右子树均不为空时,交换待删除root和minRight的键值key,再递归其右子树删除

三、二叉搜索树的应用

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

  • 比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
    在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

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

  • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;

  • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

四、二叉树进阶面试题

二叉树进阶面试题

一、根据二叉树创建字符串
二、二叉树的层序遍历
三、二叉树的最近公共祖先
四、二叉搜索树转换双向链表
五、构造二叉树
5.1 前序与中序
5.2 中序与后序
六、二叉树的前中后序遍历(非递归)
6.1 前序
6.2 中序
6.3 后序


真诚点赞,手有余香

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

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

相关文章

Springboot笔记-01

简化spring应用开发&#xff0c;约定大于配置 简化Spring应用开发的一个框架&#xff1b; 整个Spring技术栈的一个大整合&#xff1b; J2EE开发的一站式解决方案&#xff1b; 优点&#xff1a; 快速创建独立运行的spring项目以及于主流框架集成 使用嵌入式的Servlet容器&#x…

IonQ最新研究突破!引入光量子纠缠以构建量子计算网络

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨慕一 编译/排版丨沛贤 深度好文&#xff1a;700字丨5分钟阅读 2024年2月22日&#xff0c;美国量子计算公司IonQ宣布&#xff0c;公司研究团队已实现可重复地生成与离子纠缠的光子&#…

目标检测——YOLOv4算法解读

论文&#xff1a;YOLOv4&#xff1a;Optimal Speed and Accuracy of Object Detection 作者&#xff1a;Alexey Bochkovskiy, Chien-Yao Wang, Hong-Yuan Mark Liao 链接&#xff1a;https://arxiv.org/pdf/2004.10934.pdf 代码&#xff1a;https://github.com/AlexeyAB/darkne…

如何使用Excel进行设备管理:巡检、维修、保养、备件管理

在现代企业运营中&#xff0c;设备管理是维持生产效率和保障生产安全的关键环节。良好的设备管理包括设备的巡检、维修、保养和备件管理等多个方面。随着信息技术的发展&#xff0c;许多企业开始利用电子化工具来提高设备管理的效率和准确性。其中&#xff0c;Excel作为一款功能…

旅游行业分析及媒体邀约资源汇总

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 酒店旅游行业分析及媒体邀约资源汇总是两个相对独立但又相互关联的领域。下面将分别对这两个方面进行概述。 酒店旅游行业分析 1. 市场概况 市场规模&#xff1a;评估市场的总价值、增长…

react中使用antdesign组件库的table组件实现自定义筛选菜单

需求如下&#xff1a; 这是一个表格的表头信息&#xff0c;实现这几列的筛选。 涉及到的筛选有&#xff1a;自定义选择项&#xff0c;动态生成选择项&#xff0c;自定义筛选菜单展示 1、依据antdesign官方给出的例子实现的&#xff0c;比如第一列&#xff0c;使用的是&#x…

阿里云RDS MySQL与自建MySQL数据库进行主从同步(GTID方式)

1、创建数据库&#xff0c;dump阿里云RDS数据&#xff0c;导入到自建MySQL 阿里云RDS的配置几乎不用动&#xff0c;只需从RDS开一个只读的账号出来。 在ECS服务器上远程导出RDS的数据库&#xff0c;执行下面命令&#xff0c;记得将[]内容替换掉 mysqldump -h http://***.mys…

安装vcenter管理esxi

安装vcenter管理esxi虚拟化操作系统 文章目录 安装vcenter管理esxi虚拟化操作系统1.安装vcenter2.vcenter的应用 1.安装vcenter esxi虚拟机具体安装步骤请参考上一篇文章&#xff0c;vcenter软件包需自己到网上下 2.vcenter的应用

电机学(笔记一)

磁极对数p&#xff1a; 直流电机的磁极对数是指电机定子的磁极对数&#xff0c;也等于电机电刷的对数。它与电机的转速和扭矩有直接关系。一般来说&#xff0c;极对数越多&#xff0c;电机转速越低&#xff0c;扭矩越大&#xff0c;适用于低速、高扭矩的场合&#xff1b;相反&…

Java-JVM 虚拟机原理调优实战

一、基础 栈帧&#xff08;Stack Frame&#xff09;栈空间的 基本元素&#xff0c;用于 方法的调用和方法的执行的数据结构 堆内存用来存放由new创建的对象和数组。在堆中分配的内存&#xff0c;由Java虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或对象后&#xff0c…

MATLAB的使用(一)

一&#xff0c;MATLAB的编程特点 a,语法高度简化&#xff1b; b,脚本式解释型语言&#xff1b; c,针对矩阵的高性能运算&#xff1b; d,丰富的函数工具箱支持&#xff1b; e,通过matlab本体构建跨平台&#xff1b; 二&#xff0c;MATLAB的界面 工具栏:提供快捷操作编辑器…

2024年腾讯云服务器租用价格表_1个月一年3年5年和1小时费用表

腾讯云服务器租用优惠价格表&#xff1a;轻量应用服务器2核2G3M价格61元一年、2核2G4M价格99元一年&#xff0c;540元三年、2核4G5M带宽165元一年&#xff0c;2核4G5M带宽756元三年、轻量4核8G12M服务器646元15个月&#xff0c;轻量4核16G12M服务器32元1个月、312元一年&#x…

RabbitMQ介绍及搭建

架构 RabbitMQ是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff0c;使用erlang语言编写&#xff0c;依赖Erlang环境运行。 Broker&#xff1a;运行消息队列服务进程的节点&#xff0c;包含Exchange、Queue&#xff1b; Producer&#xff1a;消…

Java进阶 Maven基础

资料格式 配置文件 com.itheima Java代码 Statement stat con.createStatement(); 示例 com.itheima 命令 mvn test - Maven简介 传统项目管理状态分析 Maven 是什么 Maven的本质是一个项目管理工具&#xff0c;将项目开发过程抽象成一个项目对象模型&#xff08;POM&…

HTTP 工作流程请求响应 - 面试常问

文章目录 HTTP 工作流程请求和响应格式HTTP请求格式请求行&#xff1a;请求头部字段&#xff1a;空行&#xff1a;消息正文&#xff08;请求正文&#xff09;&#xff1a; HTTP响应格式状态行&#xff1a;响应头部字段&#xff1a;空行&#xff1a; HTTP方法HTTP状态码常用HTTP…

yocto编译测试

源码下载 git clone -b gatesgarth git://git.yoctoproject.org/poky lkmaolkmao-virtual-machine:~/yocto$ git clone -b gatesgarth git://git.yoctoproject.org/poky Cloning into poky... remote: Enumerating objects: 640690, done. remote: Counting objects: 100% (13…

整型数组按个位值排序 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 给定一个非空数组(列表)&#xff0c;其元素数据类型为整型&#xff0c;请按照数组元素十进制最低位从小到大进行排序&#xff0c;十进制最低位相同的元素&#xf…

四.排序(冒泡/选择)

目录 11-排序介绍 常见排序算法: 12-冒泡排序介绍 代码要求: 思路: 13-冒泡排序 代码: 14-选择排序 简单写法: 好的写法: 11-排序介绍 排序&#xff1a;将一组“无序”的记录序列调整为“有序”的记录序列。 列表排序&#xff1a;将无序列表变为有序列表 输入&#…

CentOS 7 编译安装 Nginx

CentOS 7 编译安装 Nginx 背景下载 Nginx 源码包安装依赖包编译添加环境变量添加守护查考文献 背景 一开始使用 docker 搭建了一个 web 服务器&#xff0c;但是由于 docker 不太方便的部署 TLS 证书&#xff0c;故使用 Nginx 做反向代理&#xff0c;实现 https 连接。 下载 N…

C#进阶实践项目(俄罗斯方块)

CSharp进阶实践项目--俄罗斯方块 1.需求分析 2.复用贪吃蛇开始等场景切换的代码 &#xff08;自己写一遍&#xff0c;不难&#xff01;&#xff01;&#xff01;&#xff09; 入口&#xff1a; 更新接口&#xff1a; Game 类&#xff1a; BeginOrEndBaseScene 类&#xff1a;…