【数据结构】二叉搜索树(二叉排序树)
🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:数据结构
目录
前言
一、什么是二叉搜索树
二、二叉搜索树的实现
节点
属性和接口的声明
插入
查找
删除
拷贝构造
析构
中序遍历
三、二叉搜索树的性能分析
总结
前言
之前,我们学习了树这一数据结构,并尝试实现了堆以及链式二叉树的相关功能:
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)_树形结构 编译器-CSDN博客
【数据结构】二叉树(c语言)(附源码)_二叉树数据结构代码-CSDN博客
今天,我们将在简单二叉树的基础上,进一步学习一种更为复杂的结构——二叉搜索树。
之前我们利用c语言实现了顺序表、链表、二叉树等数据结构。但是在实现一些复杂数据结构时,c语言显得相对繁琐,并且容易出现代码冗余的问题。由于我们现在已经具备了一定的c++代码基础,因此在之后的数据结构学习中,我们将用c++实现。
正文开始
一、什么是二叉搜索树
二叉搜索树(Binary Search Tree),也叫二叉排序树或者二叉查找树, 是一种特殊的二叉树结构。它或者是一棵空树,或者具有以下特点:
1. 如果根节点的左子树不为空,则左子树上的所有节点都小于等于根节点的值。
2. 如果根节点的右子树不为空,则右子树上的所有节点都大于等于根节点的值。
3. 它的每一棵子树也都符合前两点特性(每棵子树也都是二叉搜索树)。
简单地说,二叉搜索树是一棵中序遍历结果有序的二叉树。
一般情况下,二叉搜索树不允许有相同的值出现。当然,你在设计二叉搜索树的时候也可以允许相同值出现,只要满足二叉搜索树的特性即可。注意:二叉搜索树不一定是一棵完全二叉树。
二、二叉搜索树的实现
接下来,我们尝试实现二叉搜索树的结构及其常用方法。
节点
首先是它的节点的结构。二叉搜索树的节点包含一个数据域和指向两孩子的指针:
//节点
template<class K>
struct BSTNode
{K _key;//数据域BSTNode<K>* _left;//指向左孩子的指针BSTNode<K>* _right;//指向右孩子的指针//节点的默认构造BSTNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}
};
可以看到,节点的定义当中,模板参数被命名为"K"。这里的"K"通常表示键(key)的类型,特别应用于key_value(键值对,可以理解为一个二元组,每一个key都有对应的value)的场景。一般情况下,key的值是不允许修改的,但是其对应的value可以修改。本次实现二叉搜索树的过程当中,我们仅实现带有key的结构,至于key_value结构,会在之后的AVL树和红黑树中实现。
属性和接口的声明
接下来是二叉搜索树类的成员变量和成员函数的声明:
//二叉搜索树
template<class K>
class BST
{typedef BSTNode<K> Node;//重命名,简化代码
public://强制生成无参构造BST() = default;//拷贝构造BST(const BST<K>& t);//析构~BST();//插入bool Insert(const K& key);//删除bool Erase(const K& key);//查找Node* Find(const K& key);//中序遍历void Inorder();
private:Node* _root = nullptr;//根节点的指针
};
插入
现在我们开始实现二叉搜索树节点的插入。为了保持搜索树的特性,有如下插入步骤:
1. 如果树为空,则直接将新节点赋值给根节点的指针。
2. 如果树不为空,则需要根据新节点的值进行搜索,如果某节点的值大于新节点,则向右走;若小于新节点,则向左走。走到空位置之后,按照值插入节点即可。
3. 如果要插入重复的值,则遇到相同值的节点之后可以向左走,也可以向右走,直到找到空位置插入。(我们的实现默认不支持插入重复值)
代码实现:
//插入
bool Insert(const K& key)
{if (_root == nullptr)//树为空,直接插入{_root = new Node(key);return true;}else//树不为空,进行搜索{Node* cur = _root;//从根节点开始搜索Node* parent = nullptr;//记录cur的父亲节点while (cur)//走到空为止{if (key < cur->_key)//值小向左走{parent = cur;cur = cur->_left;}else if (key > cur->_key)//值大向右走{parent = cur;cur = cur->_right;}else//这里不允许重复的值插入{return false;}}//此时cur走到空,进行插入cur = new Node(key);if (key < parent->_key)//值小,往左边插{parent->_left = cur;}else//值大,往右边插{parent->_right = cur;}return true;}
}
这里我们定义了一个parent指针,用于记录cur的父亲节点。当cur走到空时,parent刚好指向插入位置的父亲节点,然后与parent值进行大小比较,插入新节点。
查找
二叉搜索树的特性使其具备了较高的查找效率。查找步骤如下:
1. 从根节点开始,与要查找的值进行比较,如果要找的值比根小,则向左走,否则向右走。循环往复。
2. 如果走到空,则说明不存在,返回空指针。
3. 如果不支持插入重复值,找到后返回节点地址。
4. 如果支持插入重复值,则找到后一般返回中序遍历结果的第一个节点。
代码实现:
//查找
Node* Find(const K& key)
{Node* cur = _root;//从根节点开始搜索while (cur)//走到空为止{if (key < cur->_key)//值小向左走{cur = cur->_left;}else if (key > cur->_key)//值大向右走{cur = cur->_right;}else//相等说明找到了,返回节点地址{return cur;}}//走到空说明没找到,返回空指针return nullptr;
}
删除
删除节点是所有接口中实现难度最大的一个。我们默认需要按值来删除元素,所以要先进行查找,找到之后再删除该节点。删除节点之后,为了保持二叉搜索树的原有特性,就需要调整其他节点。根据被删除的节点,有四种情况需要分析讨论:
1. 被删除节点的左右孩子均为空
2. 被删除的节点只有左孩子
3. 被删除的节点只有右孩子
4. 被删除的节点既有左孩子,又有右孩子
这里说明一下寻找右子树最小值的方法:从右孩子开始,一直访问其左孩子节点,直到访问到空为止。这里访问到的最后一个节点即是右子树的最小值。(左子树最大值也同理)
接下来,我们尝试实现删除节点的操作:
//删除
bool Erase(const K& key)
{//首先按值查找要被删除的节点Node* cur = _root;Node* parent = nullptr;//记录父亲节点while (cur){if (key < cur->_key)//值小向左走{parent = cur;cur = cur->_left;}else if (key > cur->_key)//值大向右走{parent = cur;cur = cur->_right;}else//找到了,开始删除{//有一个左孩子的情况 和 没有孩子的情况if (cur->_right == nullptr){//特殊情况,要删除的节点是根节点if (cur == _root){_root = cur->_left;//直接让根指针指向左孩子}else{//先判断自己是父亲节点的哪一个孩子//然后将左孩子托付给父亲节点if (cur == parent->_left)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;//删除该节点return true;}//有一个右孩子的情况else if (cur->_left == nullptr){//特殊情况,要删除的节点是根节点if (cur == _root){_root = cur->_right;//直接让根指针指向右孩子}else{//先判断自己是父亲节点的哪一个孩子//然后将右孩子托付给父亲节点if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;//删除该节点return true;}//有两个孩子的情况else{//首先寻找右子树的最小值Node* rightMin = cur->_right;//从右孩子开始搜索Node* rightMinParent = cur;//记录最小值的父亲节点while (rightMin->_left)//left走到空,rightMin即为右子树的最小值{rightMinParent = rightMin;rightMin = rightMin->_left;//一直向左走,找最小值}//将最小值赋值给被删除节点cur->_key = rightMin->_key;//将替代节点的右孩子托付给父亲//先判断自己是父亲节点的哪一个孩子if (rightMin == rightMinParent->_left)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;//删除该节点return true;}}}//没找到,返回falsereturn false;
}
这里有两点需要注意:
1. 第一种情况可以和第二/第三种情况合并,将空指针托付给父亲节点不会影响整体操作。
2. 第四种情况当中,如果我们找的是右子树的最小值,那么它或是叶子节点(第一种情况),或只有右孩子(第三种情况),不会有左孩子;如果我们找的是左子树的最大值,那么它或是叶子节点(第一种情况),或只有左孩子(第二种情况),不会有右孩子。所以代码中可以非常确定托付哪一个孩子。
拷贝构造
接下来我们实现拷贝构造。该接口比较简单,直接递归拷贝即可。代码如下:
//拷贝构造
BST(const BST<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;//返回根节点
}
析构
与拷贝构造相同,析构时递归释放每一个节点。代码如下:
//析构
~BST()
{_Destroy(_root);
}
void _Destroy(Node* root)
{if (root == nullptr){return;}_Destroy(root->_left);//销毁左子树_Destroy(root->_right);//销毁右子树delete root;//删除根节点
}
中序遍历
由于二叉搜索树的中序遍历结果是有序的,所以我们来实现一个中序遍历接口。中序遍历的代码与传统的二叉树完全相同。
实现如下:
//中序遍历
void Inorder()
{_Inorder(_root);cout << endl;
}
void _Inorder(Node* root)
{if (root == nullptr){return;}_Inorder(root->_left);//遍历左子树cout << root->_key << ' ';//访问根节点_Inorder(root->_right);//遍历右子树
}
最后,我们来使用一下我们实现的二叉搜索树:
int main()
{BST<int> t;int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };for (auto& e : a)//循环插入{t.Insert(e);t.Inorder();}cout << endl;for (auto& e : a)//循环删除{t.Erase(e);t.Inorder();}return 0;
}
运行结果:
程序全部代码
二叉搜索树的全部实现代码如下:
#include <iostream>
using namespace std;//节点
template<class K>
struct BSTNode
{K _key;//数据域BSTNode<K>* _left;//指向左孩子的指针BSTNode<K>* _right;//指向右孩子的指针//节点的默认构造BSTNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}
};//二叉搜索树
template<class K>
class BST
{typedef BSTNode<K> Node;//重命名,简化代码
public://强制生成无参构造BST() = default;//拷贝构造BST(const BST<K>& t){_root = _Copy(t._root);}//析构~BST(){_Destroy(_root);}//插入bool Insert(const K& key){if (_root == nullptr)//树为空,直接插入{_root = new Node(key);return true;}else//树不为空,进行搜索{Node* cur = _root;//从根节点开始搜索Node* parent = nullptr;//记录cur的父亲节点while (cur)//走到空为止{if (key < cur->_key)//值小向左走{parent = cur;cur = cur->_left;}else if (key > cur->_key)//值大向右走{parent = cur;cur = cur->_right;}else//这里不允许重复的值插入{return false;}}//此时cur走到空,进行插入cur = new Node(key);if (key < parent->_key)//值小,往左边插{parent->_left = cur;}else//值大,往右边插{parent->_right = cur;}return true;}}//删除bool Erase(const K& key){//首先按值查找要被删除的节点Node* cur = _root;Node* parent = nullptr;//记录父亲节点while (cur){if (key < cur->_key)//值小向左走{parent = cur;cur = cur->_left;}else if (key > cur->_key)//值大向右走{parent = cur;cur = cur->_right;}else//找到了,开始删除{//有一个左孩子的情况 和 没有孩子的情况if (cur->_right == nullptr){//特殊情况,要删除的节点是根节点if (cur == _root){_root = cur->_left;//直接让根指针指向左孩子}else{//先判断自己是父亲节点的哪一个孩子//然后将左孩子托付给父亲节点if (cur == parent->_left)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;//删除该节点return true;}//有一个右孩子的情况else if (cur->_left == nullptr){//特殊情况,要删除的节点是根节点if (cur == _root){_root = cur->_right;//直接让根指针指向右孩子}else{//先判断自己是父亲节点的哪一个孩子//然后将右孩子托付给父亲节点if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;//删除该节点return true;}//有两个孩子的情况else{//首先寻找右子树的最小值Node* rightMin = cur->_right;//从右孩子开始搜索Node* rightMinParent = cur;//记录最小值的父亲节点while (rightMin->_left)//left走到空,rightMin即为右子树的最小值{rightMinParent = rightMin;rightMin = rightMin->_left;//一直向左走,找最小值}//将最小值赋值给被删除节点cur->_key = rightMin->_key;//将替代节点的右孩子托付给父亲//先判断自己是父亲节点的哪一个孩子if (rightMin == rightMinParent->_left)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;//删除该节点return true;}}}//没找到,返回falsereturn false;}//查找Node* Find(const K& key){Node* cur = _root;//从根节点开始搜索while (cur)//走到空为止{if (key < cur->_key)//值小向左走{cur = cur->_left;}else if (key > cur->_key)//值大向右走{cur = cur->_right;}else//相等说明找到了,返回节点地址{return cur;}}//走到空说明没找到,返回空指针return nullptr;}//中序遍历void Inorder(){_Inorder(_root);cout << endl;}
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){if (root == nullptr){return;}_Destroy(root->_left);//销毁左子树_Destroy(root->_right);//销毁右子树delete root;//删除根节点}void _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->_left);//遍历左子树cout << root->_key << ' ';//访问根节点_Inorder(root->_right);//遍历右子树}Node* _root = nullptr;//根节点的指针
};
三、二叉搜索树的性能分析
接下来,我们简单分析一下二叉搜索树的性能。
设二叉树的节点数为N:
在较好情况下,二叉搜索树接近于完全二叉树的状态,它的高度为log₂N;
在极端情况下,二叉搜索树是单支树的状态,高度为N。
综合而言, 二叉搜索树增、删、改的时间复杂度为O(N)。
在较好情况下,其增删改的效率可接近于O(logN),但是它的效率受高度影响较大。所以传统的二叉搜索树显然不满足我们高效查找的需求。之后我们会学习自平衡的二叉搜索树(如AVL树、红黑树),它们会尽量降低二叉搜索树的高度,提高效率。
总结
今天我们学习了一种特殊的二叉树结构--二叉搜索树,它的特性使其具备了较好的查找效率。掌握二叉搜索树的知识,将为我们后续学习STL中的set和map容器打下坚实的基础。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤
相关文章:
【数据结构】二叉搜索树(二叉排序树)
🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:数据结构 目录 前言 一、什么是二叉搜索树 二、二叉搜索树的实现 节点 属性和接口的声明 插入 查找 删除 拷贝构造 析构 中序遍历 三、二叉搜索树的…...
文件的摘要算法(md5、sm3、sha256、crc)
为了校验文件在传输中保证完整性和准确性,因此需要发送方先对源文件产生一个校验码,并将该值传输给接收方,将附件通过ftph或http方式传输后,由接收方使用相同的算法对接收文件再获取一个新的校验码,将该值和发送方传的…...
Python实现人生重开模拟器
目录 人生重开模拟器介绍 代码实现 打印初始界面 设置初始属性 设置角色性别 设置角色出生点 针对每一岁,生成人生经历 完整代码 人生重开模拟器介绍 人生重开模拟器 是之前比较火的一个小游戏,我们这里使用 Python 实现一个简化版的 人生重开模…...
机器学习(二十五):决策树算法以及决策树和神经网络的对比
一、决策树集合 单一决策树会对训练数据的变化很敏感。例子:输入十个数据,判断是否是猫。只替换其中一个数据,信息增益最高的分裂特征就发生了改变,决策树就发生了变化。 使用决策树集合可以使算法更加健壮。例子:使用…...
k8s运行运行pod报错超出文件描述符表限制
1.问题描述 运行pod超过文件描述符表 unable to allocate file descriptor table - out of memory/opt/COMMAND.sh: line 9: 2.查看设备的文件描述符限制 操作前一定要先查询这个值,2097152这个值即为我们可设置的最大值,超过这个值后将无法登录&am…...
非常简单实用的前后端分离项目-仓库管理系统(Springboot+Vue)part 2
七、创建前端项目 你下载了nodejs吗?从cn官网下载:http://nodejs.cn/download/,或者从一个国外org网站下载,选择自己想要的版本https://nodejs.org/download/release/,双击下载好的安装文件,选择安装路径安…...
开源 AI 智能名片 2 + 1 链动模式 S2B2C 商城小程序源码助力品牌共建:价值、策略与实践
摘要:在当今数字化商业环境下,品牌构建已演变为企业与消费者深度共建的过程。本文聚焦于“开源 AI 智能名片 2 1 链动模式 S2B2C 商城小程序源码”,探讨其如何融入品牌建设,通过剖析品牌价值构成,阐述该技术工具在助力…...
微信小程序中的WXSS与CSS的关系及使用技巧
微信小程序中的WXSS与CSS的关系及使用技巧 引言 在微信小程序的开发中,样式的设计与实现是构建用户友好界面的关键。微信小程序使用WXSS(WeiXin Style Sheets)作为其样式表语言,WXSS在语法上与CSS非常相似,但也有一些独特的特性。本文将深入探讨WXSS与CSS的关系,介绍WX…...
STM32的CAN波特率计算
公式: CAN波特率 APB总线频率 / (BRP分频器 1)/ (SWJ BS1 BS2) SWJ一般为1。 例如STM32F407的,CAN1和CAN2都在在APB1下,频率是42000000 如果想配置成1M波特率,则计算公式为:...
【LeetCode面试150】——57插入区间
博客昵称:沈小农学编程 作者简介:一名在读硕士,定期更新相关算法面试题,欢迎关注小弟! PS:哈喽!各位CSDN的uu们,我是你的小弟沈小农,希望我的文章能帮助到你。欢迎大家在…...
活着就好20241128
早晨问候: 亲爱的朋友们,大家早上好!今天是2024年11月28日,第48周的第四天,也是十一月的第二十八天,农历甲辰[龙]年十月廿四。在这个即将步入月末、阳光依旧明媚的清晨,愿第一缕阳光轻轻洒落在…...
【kafka03】消息队列与微服务之Kafka 读写数据
Kafka 读写数据 参考文档 Apache Kafka 常见命令 kafka-topics.sh #消息的管理命令 kafka-console-producer.sh #生产者的模拟命令 kafka-console-consumer.sh #消费者的模拟命令 创建 Topic 创建topic名为 chen,partitions(分区)为3࿰…...
【Agorversev1.1数据转换】Agorverse高清地图转OpenStreetMap及SUMO路网
文章目录 Agorverse高清地图转OpenStreetMap及SUMO路网1. Agorverse osm转换说明2. 转换源码3. 处理效果4. SUMO-Carla联合仿真 Agorverse高清地图转OpenStreetMap及SUMO路网 1. Agorverse osm转换说明 根据作者的描述,其高清地图的osm文件与标准osm的区别在于以下…...
Vue 3 实现高性能拖拽指令的最佳实践
前言 在现代前端开发中,拖拽功能是增强用户体验的重要手段之一。本文将详细介绍如何在 Vue 3 中封装一个拖拽指令(v-draggable),并通过实战例子演示其实现过程。通过这篇教程,您将不仅掌握基础的拖拽功能,…...
AIGC--------AIGC在医疗健康领域的潜力
AIGC在医疗健康领域的潜力 引言 AIGC(Artificial Intelligence Generated Content,人工智能生成内容)是一种通过深度学习和自然语言处理(NLP)等技术生成内容的方式。近年来,AIGC在医疗健康领域展现出了极…...
Apache Calcite - calcite jdbc驱动使用场景
前言 在使用Calcite查询数据时通常会用到这些代码获取schema Connection connection DriverManager.getConnection("jdbc:calcite:", info); CalciteConnection calciteConnection connection.unwrap(CalciteConnection.class); SchemaPlus rootSchema calciteC…...
IEC61850实现方案和测试-4-MMS协议
IEC61850实现方案和测试-4作为介绍实现方案和测试的第四篇文章,后续会继续更新,欢迎关注。前三篇如下 第一篇是:IEC61850实现方案和测试-1-CSDN博客 第二篇是:IEC61850实现方案和测试-2-UCA-CSDN博客 第三篇是:IEC6…...
【ubuntu24.04】GTX4700 配置安装cuda
筛选显卡驱动显卡驱动 NVIDIA-Linux-x86_64-550.135.run 而后重启:最新的是12.6 用于ubuntu24.04 ,但是我的4700的显卡驱动要求12.4 cuda...
时间的礼物:如何珍视每一刻
《时间的礼物:如何珍视每一刻》 夫时间者,宇宙之精髓,生命之经纬,悄无声息而流转不息,如织锦之细线,串联古今,贯穿万物。 人生短暂,犹如白驹过隙,倏忽而逝,…...
componentReceivePropsreact class生命周期
componentReceiveProps并不是有props的变化触发,而是由父组件的更新触发的 父组件导致组件重新渲染,即使props没有更改,也会调用componentReceiveProps这个方法;如果只想处理更改,确保当前值与变更值比较--官方 …...
快速理解微服务中Sentinel怎么实现限流
Sentinel是通过动态管理限流规则,根据定义的规则对请求进行限流控制。 一.实现步骤 1.定义资源:在Sentinel中,资源可以是URL、方法等,用于标识需要进行限流的请求;(在Sentinel中,需要我们去告诉Sentinel哪些…...
25.100ASK_T113-PRO 测试摄像头(型号)
1.摄像头 USB2.0 摄像头,支持 UVC协议, 就是V4L2 USB2.0 大概可这样理解吧.这个是2K分辨率. 2.8mm焦距. 开发板还是 100ASK_T113-PRO V1.2版 2.查看摄像头驱动挂载情况 这样接好. 看看设备有没有挂载上 # ls /dev/video* /dev/video0 /dev/video1 这两个就是USB摄像头.说…...
20241127 给typecho文章编辑附件 添加视频 图片预览
Typecho在写文章时,如果一次性上传太多张图片可能分不清哪张,因为附件没有略缩图,无法实时阅览图片,给文章插入图片时很不方便。 编辑admin/file-upload.php 大约十八行的位置 一个while 循环里面,这是在进行html元素更新操作,在合…...
StarRocks-join优化
1、背景 有两个大表,都是6kw级别上下的,通过SR然后包装了一个接口对外提供查询,当前的问题是,这样大的join查询会导致BE直接宕机。并且这个sql很有代表性,我截图如下: 这个表是个单分区,所以直接…...
如何通过ChatGPT提高自己的编程水平
在编程学习的过程中,开发者往往会遇到各种各样的技术难题和学习瓶颈。传统的学习方法依赖书籍、教程、视频等,但随着技术的不断发展,AI助手的崛起为编程学习带来了全新的机遇。ChatGPT,作为一种强大的自然语言处理工具,…...
实时数据开发 | checkpoints监控和调优
监控Checkpoints 监控 checkpoint 行为最简单的方法是通过 UI 的 checkpoint 部分。 监控这两个指标: 算子收到第一个 checkpoint barrier 的时间。当触发 checkpoint 的耗费时间一直很高时,这意味着 checkpoint barrier 需要很长时间才能从 source 到达 operator…...
面试手撕题积累
1、实现滑动窗口限流,允许每分钟最多有100个请求 阿里一面题。 核心: 时间窗口管理:滑动窗口会根据时间流逝不断更新,需要记录请求的时间戳,并根据当前时间计算窗口内的请求数量。 限流判断:每次请求到来…...
开发中使用UML的流程_05 PIM-1:分析系统流程
目录 1、概述 2、PIM生成的过程 3、用例叙述格式 4、用例关系 5、执行流程: 6、惯用的编号方式 1、概述 在进入到PIM阶段之后,系统分析员将所有系统用例依相关性分成若干组,以组别方式生成该组系统用例涉及的PIM-1---PIM-4产生结果&am…...
【Go】-go中的锁机制
目录 一、锁的基础知识 1. 互斥量/互斥锁 2. CAS(compare and swap) 3. 自旋锁 4. 读写锁 5. 乐观锁 & 悲观锁 6. 死锁 二、go中锁机制 1. Mutex-互斥锁 2. RWMutex-读写锁 2.1 RWMutex流程概览 2.2 写锁饥饿问题 2.3. golang的读写锁源…...
Scala学习记录,全文单词统计
package test32 import java.io.PrintWriter import scala.io.Source //知识点 // 字符串.split("分隔符":把字符串用指定的分隔符,拆分成多个部分,保存在数组中) object test {def main(args: Array[String]): Unit {//从文件1.t…...
重构项目架构
前言 我们上篇文章对整个项目进行一个整体的规划,其中对于APP类规划了类,本篇文章我们就来实现这个规划; class App {//加载页面constructor() {}//获取位置_getPosition() {}//接受位置_loadMap() {}//在地图上点击展现表单_showForm() {}/…...
一个开源轻量级的服务器资源监控平台,支持告警推送
大家好,今天给大家分享一款开源的轻量级服务器资源监控工具Beszel,提供历史数据记录、Docker容器统计信息监控以及多种警报功能,用于监控服务器资源。 项目介绍 Beszel由hub(中心服务器端应用,基于PocketBase构建&…...
介绍一下atof(arr);(c基础)
hi , I am 36 适合对象c语言初学者 atof(arr);是返回浮点数(double型),浮点数数是arr数组中字符中数字 格式 #include<stdio.h> atof(arr); 返回值arr数组中的数 未改变arr数组 #include<stdio.h> //atof(arr) 返 <stdlib> int…...
基于微信小程序的平价药房管理系统+LW参考示例
1.项目介绍 系统角色:管理员、医生、普通用户功能模块:用户管理、医生管理、药品分类管理、药品信息管理、在线问诊管理、生活常识管理、日常提醒管理、过期处理、订单管理等技术选型:SpringBoot,Vue,uniapp等测试环境…...
什么是 C++ 中的函数对象?它有什么特点?如何定义和使用函数对象?数对象与普通函数有什么区别?
在 C 中,函数对象(Function Object)也被称为仿函数(Functor),是一种可以像函数一样被调用的对象,是一个类的对象,该类重载了函数调用运算符operator()。 函数对象的特点: 与普通函数…...
JAVA篇05 —— 内部类(Local、Anonymous、Member、Static)
欢迎来到我的主页:【一只认真写代码的程序猿】 本篇文章收录于专栏【小小爪哇】 如果这篇文章对你有帮助,希望点赞收藏加关注啦~ 目录 1 内部类Inner Class 1.1 局部内部类 1.2 匿名内部类(※※) 1.3 匿名类最佳实践…...
vmware安装ubuntu22.04 复制黏贴 上网
1、ubuntu下载 [Download - 清华镜像站]: 点击 清华大学开源软件镜像站 - Ubuntu 22.04.4 下载 页面中的 ubuntu-22.04.4-desktop-amd64.iso清华大学开源软件镜像站 - Ubuntu 22.04.4 下载 2、安装向导 3、网络设置 sudo netplan try sudo netplan apply4、复制…...
Spring Bean初始化流程
首先: 加载Bean定义(Configuration) 然后对于每个Bean: 1、实例化Bean(应该是从Bean方法中获取,Bean方法里面包含new这个类型的代码)2、依赖注入(所依赖的Bean要经历相同的流程)、调用Setter…...
C 语言函数递归探秘:从基础概念到复杂问题求解的进阶之路
我的个人主页 我的专栏:C语言,希望能帮助到大家!!!点赞❤ 收藏❤ 目录 什么是函数递归递归的基本组成递归的工作原理递归的优缺点递归的经典案例 5.1 阶乘计算5.2 斐波那契数列5.3 汉诺塔问题5.4 二分查找 递归的高级…...
【Zookeeper】三,Zookeeper的安装与基本操作
文章目录 安装Zookeeper下载解压解压后的目录结构运行Zookeeper 基本操作 安装Zookeeper 下载 官网下载Zookeeper,会得到一个tar包,如:apache-zookeeper-3.8.4-bin.tar.gz 解压 tar -xvf apache-zookeeper-3.8.4-bin.tar.gz -C /usr/loca…...
STL算法之数值算法<stl_numeric.h>
这一节介绍的算法,统称为数值(numeric)算法。STL规定,欲使用它们,客户端必须包含头文件<numeric>.SGI将它们实现与<stl_numeric.h>文件中。 目录 运用实例 accumulate adjacent_difference inner_product partial_sum pow…...
Git 入门超简单指南
1. 什么是 Git? Git 是一个分布式版本控制系统,由 Linus Torvalds 于 2005 年创建。它的主要目的是帮助开发者有效地管理和跟踪项目的历史版本。通过使用 Git,你可以轻松地记录每一次代码的修改,回滚到以前的版本,以及…...
UE5 和 UE4 中常用的控制台命令总结
调用控制台 按下键盘上的 ~ 键可以调用控制台命令。 技巧 使用键盘的 ↑ 键可以查看之前输入过的指令。控制台指令并不需要打全名,输入空格后跟随指令的部分字符可以进行模糊搜索。按下 Ctrl Shift , 打开 GPUProfile 面板。 命令如下: 调试类 s…...
[护网杯 2018]easy_tornado
这里有一个hint点进去看看,他说md5(cookie_secretmd5(filename)),所以我们需要获得cookie_secret的value 根据题目tornado,它可能是tornado的SSTI 这里吧filehash改为NULL. 是tornado的SSTI 输入{{handler.settings}} (settings 属性是一个字典&am…...
论 ONLYOFFICE:开源办公套件的深度探索
公主请阅 引言第一部分:ONLYOFFICE 的历史背景1.1 开源软件的崛起1.2 ONLYOFFICE 的发展历程 第二部分:ONLYOFFICE 的核心功能2.1 文档处理2.2 电子表格2.3 演示文稿 第三部分:技术架构与兼容性3.1 技术架构3.2 兼容性 第四部分:部…...
华为OD机试真题---幼儿园篮球游戏
华为OD机试真题中的“幼儿园篮球游戏”是一道有趣的逻辑模拟题。以下是该题目的详细描述及解题思路: 题目描述 幼儿园里有一个放倒的圆桶,它是一个线性结构。允许在桶的右边将篮球放入,可以在桶的左边和右边将篮球取出。每个篮球有单独的编…...
C#基础控制台程序
11.有一个54的矩阵,要求编程序求出其中值最大的那个元素的值,以及其所在的行号和列号。 12.从键盘输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。 13.输入一个数,判断它是奇数还是偶数,如果…...
CASS插入多行文字
问题描述 有时在DWG文件中需要标注多行文字,文字注记只是单行的。 解决办法 工具栏中选择文字——>写文字。快捷键是 MTEXT 可以自行换行,并设置相关格式。 当需要把多行文字转换成单行文字的时候,使用下列功能。可以将多行文字变成…...
【青牛科技】D1671 75Ω 带4级低通滤波的单通道视频放大电 路芯片介绍
概 述 : D1671是 一 块 带 4级 低 通 滤 波 的 单 通 道 视 频 放 大 电 路 , 可 在3V或5V的 低 电 压 下 工 作 。 该 电 路 用 在 有 TV影 象 输 出 功 能 的 产 品 上 面,比如 机 顶 盒 ,监 控 摄 象 头 ,DVD&#…...
基于stm32的智能教室管理系统/智能家居系统
基于stm32的智能教室管理系统/智能家居系统 持续更新,欢迎关注!!! ** 基于stm32的智能教室管理系统/智能家居系统 ** 目前,物联网已广泛应用在我们的生活中。智慧校园是将校园中的生活、学习、工作等相关的资源联系在一起,实现管理的智能化…...