数据结构_进阶(1):搜索二叉树

1.内容

建议再看这节之前能对C++有一定了解

二叉树在前面C的数据结构阶段时有出过,现在我们对二叉树来学习一些更复杂的类型,也为之后C++学习的 map 和 set 做铺垫

  • 1. map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构
  • 2. 二叉搜索树的特性了解,有助于更好的理解map和set的特性
  • 3. 有些OJ题使用C语言方式实现比较麻烦,比如有些地方要返回动态开辟的二维数组。

因此本节文章所涉及到的知识点有很多都会与C++有关

2.搜索二叉树:

🍉搜索二叉树的概念:

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

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

🍉搜索二叉树的操作

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

🍒二叉搜索树的查找

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

🍒搜索二叉树的插入:

插入的具体过程如下:

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

🍒搜索二叉树的删除

删除时搜索二叉树最复杂的地方,他只要分为三种情况:

1. 删除叶子节点

也就是左右都无孩子的节点,直接删除就行

2. 删除只有一个孩子的节点

左孩子为空,或者右孩子为空

这种情况需要将他们的下一个孩子节点接到其父节点上

3.删除两边都有孩子的节点

如删除 8 或者 3

这种清空就比较复杂,我们用替换原则,找左树的最大节点或者去找右树的最小节点来替换。

比如我们要删除8,就需要找左树的最大节点进行替换,左树的最大节点是7,我们将其与放到8的位置,将6的右节点置为空即可

🍉搜索二叉树的实现:

1.创建:

template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key) //初始化列表进行初始化:_left(nullptr), _right(nullptr), _key(key){}
};

2. 插入操作:

   	bool Insert(const K& key){// 1.先判断跟为空if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;// 利用循环判断cur节点的值与插入值的大小,来缺点插入值放到哪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.查找操作:

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

4.打印操作:

	// 利用递归打印,因为类外拿不到_root,// 所以给递归又加了个嵌套函数,用该类内的函数去调_rootvoid InOrder(){_InOrder(_root);cout << endl;}// 直接写这个函数,在类外面不能调_root,所以传参比较困难// 因为_root是私有成员,所以序号再套上面的一层函数void _InOrder(Node* root){if (root == nullptr)return;// 中序遍历的逻辑打印_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}

5.删除操作

上面所说的删除叶子节点的情况,可以直接放到入到第二种情况下一并解决,

在第二种情况的时候需要注意:

这种情况要去判断要删除的节点是否为根节点,如果是就直接把下面的孩子节点换成根节点就行

bool Erase(const K& key){Node* parent = nullptr;Node* cur = Find(key);while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除// 1、左为空// 如果此时根节点只有一个孩子,此时要删除根节点,// 就不会进入之前的判断,会导致parent为空的空指针问题// 可看上图了解if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;} // 2、右为空// 如果此时根节点只有一个孩子,此时要删除根节点,// 就不会进入之前的判断,会导致parent为空的空指针问题else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 找右树最小节点替代,也可以是左树最大节点替代// 这里我们用的是右数的最小节点Node* pminRight = cur;	// pminRight是minRight的父节点Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;}return true;}}return false;}

🍒源代码如下

#include <iostream>
using namespace std;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:// 插入bool Insert(const K& key){// 1.先判断跟为空if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;// 利用循环判断cur节点的值与插入值的大小,来缺点插入值放到哪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 = Find(key);while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除// 1、左为空// 如果此时根节点只有一个孩子,此时要删除根节点,// 就不会进入之前的判断,会导致parent为空的空指针问题if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;} // 2、右为空// 如果此时根节点只有一个孩子,此时要删除根节点,// 就不会进入之前的判断,会导致parent为空的空指针问题else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 找右树最小节点替代,也可以是左树最大节点替代Node* pminRight = cur;		// pminRight是minRight的父节点Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;}return true;}}return false;}// 打印void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root = nullptr;
};//测试
void TestBSTree()
{int a[] = { 8, 3, 1, 6, 4, 7, 10, 14, 13 };BSTree<int> t1;// 循环插入for (auto e : a){t1.Insert(e);}t1.InOrder();t1.Erase(13);t1.Erase(14);t1.Erase(10);t1.Erase(4);t1.Erase(6);t1.Erase(3);t1.InOrder();
}
int main()
{TestBSTree();return 0;
}

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

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

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

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

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:$log_2 N$
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:$\frac{N}{2}$

如果退化成单支树,二叉搜索树的性能就失去了。
那能否进行改进,不论按照什么次序插 入关键码,二叉搜索树的性能都能达到最优?

所以我们后面还会对AVL树和红黑树做为重点学习

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

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

相关文章

Stable Diffusion生成图片参数查看与抹除

前几天分享了几张Stable Diffusion生成的艺术二维码&#xff0c;有同学反映不知道怎么查看图片的参数信息&#xff0c;还有的同学问怎么保护自己的图片生成参数不会泄露&#xff0c;这篇文章就来专门分享如何查看和抹除图片的参数。 查看图片的生成参数 1、打开Stable Diffus…

Ubuntu 安装 Docker

本文目录 1. 卸载旧版本 Docker2. 更新及安装工具软件2.1 更新软件包列表2.2 安装几个工具软件2.3 增加一个 docker 的官方 GPG key2.4 下载仓库文件 3. 安装 Docker3.1 再次更新系统3.2 安装 docker-ce 软件 4. 查看是否启动 Docker5. 验证是否安装成功 1. 卸载旧版本 Docker …

【iOS】—— 属性关键字及weak关键字底层原理

文章目录 先来看看常用的属性关键字有哪些&#xff1a;内存管理有关的的关键字&#xff1a;&#xff08;weak&#xff0c;assign&#xff0c;strong&#xff0c;retain&#xff0c;copy&#xff09;关键字weak关键字assignweak 和 assign 的区别&#xff1a;关键字strong&#…

React(3)

1.案例选项卡 import React, { Component } from reactexport default class App extends Component {state{tabList:[{id:1,text:"电影"},{id:2,text:"影院"},{id:3,text:"我的"}]}render() {return (<div><ul>{this.state.tabList…

【LocalSend】开源跨平台的局域网文件传输工具,支持IOS、Android、Mac、Windows、Linux

工作前提条件&#xff1a;设备使用相同的局域网。 LocalSend is a cross-platform app that enables secure communication between devices using a REST API and HTTPS encryption. Unlike other messaging apps that rely on external servers, LocalSend doesn’t require …

【经济调度】基于多目标宇宙优化算法优化人工神经网络环境经济调度研究(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码及数据 &#x1f4a5;1 概述 基于多目标宇宙优化算法&#xff08;Multi-Objective Universe Optimization Algorithm, MOUA&#xff09;优化人工神经网络环境经济调度是一…

预付费电表收费系统

预付费电表收费系统是一种先进的电表管理系统&#xff0c;它能够帮助电力公司更加高效地管理电表收费&#xff0c;提高用电效率&#xff0c;降低能源浪费。本文将从以下几个方面介绍预付费电表收费系统的特点和优势。 一、预付费电表收费系统的原理 预付费电表收费系统是指用户…

Hadoop集群启动常见错误

错误一 &#xff1a; 配置文件错误 解决方案&#xff1a;检查配置文件&#xff0c;修改错误。重新分发&#xff08;同步&#xff09; 常见错误二 &#xff1a; 重复格式化 DataNode NameNode 在格式化时如果发现下面的提示说明重复格式化了 datanode和namenode的集群id…

Spring Cloud 远程接口调用OpenFeign负载均衡实现原理详解

环境&#xff1a;Spring Cloud 2021.0.7 Spring Boot 2.7.12 配置依赖 maven依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency&…

一百三十、海豚调度器——用DolphinScheduler定时调度HiveSQL任务

一、目标 用海豚调度器对Hive数仓各层数据库的SQL任务进行定时调度。比如&#xff0c;DWD层脱敏清洗表的动态插入数据、DWS层指标表的动态插入数据 二、工具版本 1、海豚调度器&#xff1a;apache-dolphinscheduler-2.0.5-bin.tar.gz 2、Hive&#xff1a;apache-hive-3.1.2…

长短期记忆网络(LSTM)原理解析

长短期记忆网络&#xff08;Long Short-Term Memory&#xff0c;简称LSTM&#xff09;是一种常用于处理序列数据的深度学习模型。它在循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;的基础上进行了改进&#xff0c;旨在解决传统RNN中的梯度消失…

PyTorch训练RNN, GRU, LSTM:手写数字识别

文章目录 pytorch 神经网络训练demoResult参考来源 pytorch 神经网络训练demo 数据集&#xff1a;MNIST 该数据集的内容是手写数字识别&#xff0c;其分为两部分&#xff0c;分别含有60000张训练图片和10000张测试图片 图片来源&#xff1a;https://tensornews.cn/mnist_intr…

Kafka消息监控管理工具Offset Explorer的使用教程

1、kafka监控管理工具 Offset Explorer是一款用于监控和管理Apache Kafka集群中消费者组偏移量的开源工具。它提供了一个简单直观的用户界面&#xff0c;用于查看和管理Kafka消费者组偏移量的详细信息。 Offset Explorer具有以下主要功能和特点&#xff1a; 实时监控&#x…

架构训练营学习笔记3-5:消息队列备选架构设计实战

本文属于架构训练营学习笔记系列&#xff1a;模块3的案例讲解 总的来说&#xff0c;这篇从更高的维度去讲&#xff0c;而不是关注消息队列的常见问题&#xff1a;比如消息如何发送&#xff0c;消息如何不丢失 &#xff0c;消息如何不重复。总体上分为2部分&#xff1a;利益干系…

【Docker】什么是Docker,它用来干什么

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

ADB初识

ADB是Android Debug Bridge&#xff0c;是一个命令行程序。abd可以从计算机上通过USB控制Android手机设备。可以使用ADB复制文件、安装和卸载应用程序&#xff0c;运行shell命令等。 ADB的下载配置 Windows版本&#xff1a;https://dl.google.com/android/repository/platform…

2.SpringBoot运维实用篇

SpringBoot运维实用篇 ​ ​ 下面就从运维实用篇开始讲&#xff0c;在运维实用篇中&#xff0c;我给学习者的定位是玩转配置&#xff0c;为开发实用篇中做各种技术的整合做好准备工作。 主要分为以下内容&#xff1a; SpringBoot程序的打包与运行配置高级多环境开发日志 ​…

C国演义 [第十二章]

第十二章 打家劫舍题目理解步骤dp数组递推公式初始化遍历顺序 代码 打家劫舍II题目理解步骤递推公式初始化遍历顺序 代码 打家劫舍 力扣链接 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋…

《深度学习推荐系统》笔记

目录 一、推荐系统是什么1.作用和意义2.推荐系统的架构2.1 逻辑架构2.2 技术架构 二、传统的推荐系统方法1. 协同过滤算法1.1 userCF&&ItemCF1.3 矩阵分解算法 2. 逻辑回归算法3. 因子分解机3.1 POLY2模型3.2 FM模型3.3 FFM模型3.4 小结 4. 组合模型4.1 GBDTLR组合模型…

通过监控平台提高运维效率、降低运营成本、实现绿色低碳、节能降耗、提升PUE值-安科瑞黄安南

01引言 近年来&#xff0c;随着母线槽在建筑及工厂的配电中越来越广泛&#xff0c;母线槽场景运用得越多&#xff0c;随着数据中心建设的快速发展和更高需求&#xff0c;智能母线系统逐渐被应用于机房的末端配电中&#xff0c;具有电流小、插接方便、智能化程度高等特点&#…