『 C++ 』BinarySearchTree搜索二叉树

请添加图片描述


文章目录

    • 前言 🦕
    • 二叉搜索树的概念 🦕
    • 搜索二叉树的初始化 🦕
    • Insert( )插入函数 🦕
      • 👾 InsertR( ) 插入函数(递归)
    • InOrder( ) 中序遍历打印 🦕
    • Find( ) 查找函数 🦕
      • 👾 Find( ) 查找函数(递归)
    • Erase( ) 删除函数 🦕
      • 👾 Erase( ) 函数(递归)
    • 默认成员函数 🦕
      • 👾 构造函数
      • 👾 拷贝构造函数
      • 👾 赋值运算符重载
      • 👾 析构函数
    • 总代码 🦕

前言 🦕

请添加图片描述
二叉树顾名思义即为一个树中节点的度不大于2的有序树,是树中结构中较为简单的一种;

其特点为一个节点中分为两个子树;

通常可以以二叉树进行分治的思路对问题进行解决(递归或非递归);

但在实际中普通二叉树由于在数据存储方面中并没有明显的特性,所以在实际中普通二叉树的应用场景并不多;

BinarySearchTree二叉搜索树,顾名思义这种类型的二叉树对搜索有着比较大的优势;


二叉搜索树的概念 🦕

请添加图片描述
二叉搜索树又称搜索二叉树,也可以称为排序树,顾名思义这棵树有着对数据进行搜索的优势;

该二叉树的性质为:

  • 如果这棵树的左子树不为空,则左子树上的节点值都小于根节点的值;
  • 如果这棵树的右子树不为空,则右子树上的节点值都小于根节点的值;

二叉搜索树的左右子树也同时满足该条件;

该二叉树的节点若是使用中序遍历的方式进行打印则将会以顺序的方式进行打印,所以这棵树也被称为排序树;

在这里插入图片描述

同时搜索二叉树中的数据还具有唯一性,即同一种的数据只能进行一次插入;


搜索二叉树的初始化 🦕

请添加图片描述
在实现当中搜索二叉树也应该使用类模板确保可以进行泛型编程;

搜索二叉树的初始化与普通二叉树的初始化相当,都需要两个类:

  • struct BSTNode
    该类用来对搜索二叉树的各个节点进行封装;
    template<class K>//一般情况下二叉树内的模板参数习惯以K代替(key);
    struct BSTNode{//构造函数BSTNode(const K& key = K())// 给定缺省参数确保可以正常进行初始化:_pleft(nullptr),//初始化列表初始化参数_pright(nullptr),_key(key){}BSTNode<K> *_pleft;//左子树BSTNode<K> *_pright;//右子树K _key;//数据
    };
    
  • class BinarySearchTree
    该类主要用于实现二叉树的整体(包括各个成员函数)
    template<class K>
    class BinarySearchTree{
    typedef BSTNode<K> Node;//对节点进行typedef重命名
    public:BinarySearchTree(){}//构造函数~BinarySearchTree(){}//析构函数BinarySearchTree(const BinarySearchtree<K> &node){}//拷贝构造函数BinarySearchTree<K>& operator=(BinarySearchTree<K> node){}//赋值运算符重载bool Insert(const K& key){}//数据插入void InOrder(){}//中序遍历打印数据bool Find(const K&key){}//查找数据bool Erase(const K&key){}//删除数据protected:
    private:Node* _proot = nullptr;//头节点
    };
    

Insert( )插入函数 🦕

请添加图片描述
插入函数的实现在搜索二叉树中一般为顺照搜索二叉树的特性进行遍历;

当遍历节点至空时则表示在该处进行插入;

  • 若是需要插入的数据大于当前节点的数据则向当前节点的右子树进行遍历;
  • 若是需要插入的数据小于当前节点的数据则向当前节点的左子树进行遍历;
  • 若是需要插入的数据等于当前节点的数据则返回false(唯一性);
  • 遍历至nullptr处则表示当前不存在节点,可进行插入,插入结束后返回true;

在这里插入图片描述

 bool Insert(const K& key){if(_proot == nullptr){//如果root节点为空说明为空树,空树进行第一次插入_proot = new Node(key);return true;//插入成功进行返回}Node* cur = _proot;Node* tmp;while(cur!=nullptr){tmp = cur;if(key>cur->_key){tmp = cur;cur = cur->_pright;}else if(key<cur->_key){tmp = cur;cur = cur->_pleft;}else{//相同 返回falsereturn false;}}if(key>tmp->_key){//链接在右边cur = new Node(key);tmp->_pright = cur;}else{//链接在左边cur = new Node(key);tmp->_pleft = cur;}return true;}

该函数的插入还存在一个链接的问题;

链接的问题采用类似双指针的方式,一个指针进行遍历确认是否进行插入,另一个指针用来保存该节点的父节点位置方便两个节点进行链接;


👾 InsertR( ) 插入函数(递归)

请添加图片描述
一般来说在类中成员函数的递归比较麻烦的是对于数据的传参问题;
所以为了解决这个问题建议在自定义类型中定义递归函数时尽量写一个子函数用于递归,并使用成员函数来调用此子函数;

该函数的递归思路与非递归的思路相当,唯一不同的是在递归中除了要思考节点的插入问题同时还需要注意节点与节点之间的链接问题,在递归当中子函数的节点参数可以使用Node*& root来代替;

由于引用算是一个变量的别名,意义即为将一个指针的别名传入下一层的递归当中,将新节点插入该节点即完成插入的节点链接问题;

  bool InsertR(const K&key){//主函数return _InsertR(key,_proot);//调用函数进行递归}//----------------------------------bool _InsertR(const K&key,Node*& root){//子函数if(root == nullptr) {root = new Node(key);return true;}if(root->_key < key){return _InsertR(key,root->_pright);}else if(root->_key>key){return _InsertR(key,root->_pleft);}else return false;}

InOrder( ) 中序遍历打印 🦕

请添加图片描述
该函数以递归的方式进行中序遍历,使用子函数递归,主函数调用的方式即可;

  void InOrder(){//主函数_InOrder(_proot);cout<<endl;}//----------------------------------void _InOrder(Node *root){//子函数if(root == nullptr){return;}_InOrder(root->_pleft);cout<<root->_key<<" ";_InOrder(root->_pright);}

Find( ) 查找函数 🦕

请添加图片描述
该函数使用非递归以搜索二叉树的规则进行遍历即可;

找到数据则返回true否则返回false;

  bool Find(const K& key){Node*pcur = _proot;while(pcur){if(key>pcur->_key)  pcur = pcur->_pright;else if(key<pcur->_key) pcur = pcur->_pleft;else return true;}return false;}

👾 Find( ) 查找函数(递归)

请添加图片描述
该函数建立子函数进行递归即可;

  bool FindR(const K& key){//主函数return _FindR(key,_proot);}//----------------------------------bool _FindR(const K&key,Node*root){//子函数if(root == nullptr) return false;  if(root->_key == key) return true;if(root->_key>key) return _FindR(key,root->_pleft);else  return _FindR(key,root->_pright); }  

Erase( ) 删除函数 🦕

请添加图片描述
删除函数在搜索二叉树中是一个比较复杂的函数;

复杂的问题在于:

  1. 该如何删除节点;
  2. 删除节点后如何对剩余节点进行链接;

同时从一张图的分析中可以分析出搜索二叉树删除节点的几种情况:

  1. 删除的节点不存在子树;

在这里插入图片描述

当所删除的节点不存在左右子树时直接将节点进行删除即可;


  1. 删除的节点左子树为空或者右子树为空;

在这里插入图片描述

当所删除的节点的左子树或者右子树为空时,可采用托孤的方式对节点进行链接;

即所删除节点的父节点与所删除的子节点进行链接;


  1. 删除的节点左右子树都不为空;

在这里插入图片描述

当所删除的节点其左右子树都不为空时,由于每个节点只能管理两个子树(左右子树),而所删除的节点存在左右两棵子树,故不能采用托孤的方式对所删除的节点的子树进行管理;

在这种情况下即可采用伪删除法的方式对节点进行伪删除;

伪删除即为选择一个符合接管该节点的左右子树规则的节点,将其值赋值给当前节点,并将选择的节点进行删除 (符合该种条件的节点一般为该节点左子树的最大值或该节点右子树的最小值);

以该图为例若是删除节点8,则可以使用节点7进行代替接管其左右子树,并将原节点7进行删除从而达到伪删除的目的;

实现:

 bool Erase(const K& key){if(nullptr == _proot) return false;//找到这个key值Node* pcur = _proot;Node* parent = _proot;while(pcur){//进行遍历找到对应的key值if(key>pcur->_key){parent = pcur;pcur = pcur->_pright;}else if(key<pcur->_key){parent = pcur;pcur = pcur->_pleft;}else{//已经找到了对应的key值,需要进行删除//左为空的情况if(pcur->_pleft == nullptr){//判断极端情况(左为空或者右为空的情况下所删节点为根)if(pcur == _proot){_proot = pcur->_pright;delete pcur;return true;}if(parent->_pleft == pcur){parent->_pleft = pcur->_pright;}else{parent->_pright = pcur->_pright;}delete pcur;return true;}//右为空的情况else if(pcur->_pright == nullptr){//判断极端情况(左为空或者右为空的情况下所删节点为根)if(pcur == _proot){_proot = pcur->_pleft;delete pcur;return true;}if(parent->_pleft == pcur){parent->_pleft = pcur->_pleft;}else {parent->_pright = pcur->_pleft;}delete pcur;return true;}//左右都不为空else{/** 找到左子树的最右子树或右子树的最左子树*/Node*tmp = pcur->_pright; //右子树的最左子树(最小值)Node*tmp_parent = pcur;while(tmp->_pleft){tmp_parent = tmp;tmp = tmp->_pleft;    }/** 找到符合条件的值,进行伪删除法;*/pcur->_key = tmp->_key;//说明该子树存在孩子,需要对该子树进行托孤//由于所找的数据为该节点右子树的最小值,说明找到的这个最小值不存在左子树,只需要对右子树进行托孤;if(tmp_parent->_pleft == tmp){tmp_parent->_pleft = tmp->_pright;delete tmp;}else{tmp_parent->_pright = tmp->_pright;delete tmp;}return true;}}}//未找到key值,pcur指针已经为空,返回false;return false;}

👾 Erase( ) 函数(递归)

请添加图片描述
对于Erase()函数的递归方式与其非递归的方式都有异曲同工之处;

唯一不同的是为了节点链接的方便在用于递归的子函数当中使用指针引用的方式完成节点的链接;

  bool EraseR(const K&key){//主函数return _EraseR(key,_proot);}//----------------------------------bool _EraseR(const K& key, Node*& root){//子函数if(root == nullptr) return false;if(key>root->_key) return _EraseR(key,root->_pright);else if(key<root->_key) return _EraseR(key,root->_pleft);else{Node *cur = root;//左为空if(root->_pleft == nullptr){root = root->_pright;delete cur;return true;} //右为空 else if(root->_pright == nullptr){root = root->_pleft;delete cur;return true;}//左右都不为空else{cur = root->_pleft; while(cur->_pright) cur = cur->_pright;root->_key = cur->_key;_EraseR(cur->_key,root->_pleft);return true; } }
}

默认成员函数 🦕

👾 构造函数

请添加图片描述
搜索二叉树的构造函数即对节点进行初始化即可,在不存在其他的构造函数的情况下可以使用默认生成的构造函数,因为由于在定义成员变量时给定了成员变量了缺省参数_proot = nullptr;

若是存在其他构造函数 (拷贝构造函数也为构造函数的一种) 时则需要手写这个构造函数;
或是使用default关键字:

BinarySearchTree() = default;

👾 拷贝构造函数

请添加图片描述
拷贝构造函数则可以使用递归的方式逐个对节点进行拷贝从而达到深拷贝的效果;

  BinarySearchTree(const BinarySearchTree<K> &node){//主函数_proot =  _CopyNode(node._proot);}//----------------------------------Node* _CopyNode(const Node* node){//子函数if(node == nullptr) {return nullptr;}Node*newnode = new Node(node->_key);newnode->_pleft = _CopyNode(node->_pleft);newnode->_pright = _CopyNode(node->_pright);return newnode;}

👾 赋值运算符重载

请添加图片描述
赋值运算符重载一般采用现代的写法:

即为利用自定义类型在进行传值传参时会进行拷贝构造的特性完成对该自定义类型的拷贝,再进行交换与返回;

  BinarySearchTree<K>& operator=( BinarySearchTree<K> node){//在传值传参过程中调用拷贝构造生成了临时拷贝(深拷贝)swap(_proot,node._proot);return *this;}

👾 析构函数

请添加图片描述
析构函数采用父子函数的方式,利用子函数作递归,主函数调用该子函数即可;

void _Destroy(Node*& root){if(root == nullptr) return;_Destroy(root->_pleft);_Destroy(root->_pright);delete root;root = nullptr;
}//----------------------------------~BinarySearchTree(){_Destroy(_proot);
}

总代码 🦕

请添加图片描述

#pragma once #include<iostream>using namespace std;template<class K>
struct BSTNode{BSTNode(const K& key = K()):_pleft(nullptr),_pright(nullptr),_key(key){}BSTNode<K> *_pleft;BSTNode<K> *_pright;K _key;
};template<class K>
class BinarySearchTree{typedef BSTNode<K> Node;public:/** 默认成员函数*/ //--------------------------------//构造函数BinarySearchTree()=default;/*强制生成默认构造函数*///--------------------------------//拷贝构造BinarySearchTree(const BinarySearchTree<K> &node){_proot =  _CopyNode(node._proot);}//--------------------------------//赋值重载操作符BinarySearchTree<K>& operator=( BinarySearchTree<K> node){swap(_proot,node._proot);return *this;}//--------------------------------//析构函数 - 使用递归方法 后序遍历进行Destory~BinarySearchTree(){_Destroy(_proot);}//--------------------------------bool Insert(const K& key){if(_proot == nullptr){//如果root节点为空说明为空树,空树进行第一次插入_proot = new Node(key);return true;//插入成功进行返回}Node* cur = _proot;Node* tmp;while(cur!=nullptr){tmp = cur;if(key>cur->_key){tmp = cur;cur = cur->_pright;}else if(key<cur->_key){tmp = cur;cur = cur->_pleft;}else{//相同 返回falsereturn false;}}if(key>tmp->_key){//链接在右边cur = new Node(key);tmp->_pright = cur;}else{//链接在左边cur = new Node(key);tmp->_pleft = cur;}return true;}//-----------------------void InOrder(){/** 自定义类型成员函数若是需要进行递归时* 尽量设置一个子函数来方便递归并使用原成员函数进行调用* * 在该自定义类型当中,由于传的参数为private私有成员变量* 私有成员变量不方便直接进行传参,所以为了方便传参_proot* 在private访问限定符的域当中设置一个子函数用来递归* 通过原函数去调用这个私有成员函数*/_InOrder(_proot);cout<<endl;}//-----------------------bool Find(const K& key){Node*pcur = _proot;while(pcur){if(key>pcur->_key)  pcur = pcur->_pright;else if(key<pcur->_key) pcur = pcur->_pleft;else return true;}return false;}//-----------------------bool Erase(const K& key){if(nullptr == _proot) return false;//找到这个key值Node* pcur = _proot;Node* parent = _proot;while(pcur){//进行遍历找到对应的key值if(key>pcur->_key){parent = pcur;pcur = pcur->_pright;}else if(key<pcur->_key){parent = pcur;pcur = pcur->_pleft;}else{//已经找到了对应的key值,需要进行删除//左为空的情况if(pcur->_pleft == nullptr){if(pcur == _proot){_proot = pcur->_pright;delete pcur;return true;}if(parent->_pleft == pcur){parent->_pleft = pcur->_pright;}else{parent->_pright = pcur->_pright;}delete pcur;return true;}//右为空的情况else if(pcur->_pright == nullptr){//判断极端情况(左为空或者右为空的情况下所删节点为根)if(pcur == _proot){_proot = pcur->_pleft;delete pcur;return true;}if(parent->_pleft == pcur){parent->_pleft = pcur->_pleft;}else {parent->_pright = pcur->_pleft;}delete pcur;return true;}//左右都不为空else{/** 找到左子树的最右子树或右子树的最左子树*/Node*tmp = pcur->_pright; //右子树的最左子树(最小值)Node*tmp_parent = pcur;while(tmp->_pleft){tmp_parent = tmp;tmp = tmp->_pleft;    }/** 找到符合条件的值,进行伪删除法;*/pcur->_key = tmp->_key;//说明该子树存在孩子,需要对该子树进行托孤//由于所找的数据为该节点右子树的最小值,说明找到的这个最小值不存在左子树,只需要对右子树进行托孤;if(tmp_parent->_pleft == tmp){tmp_parent->_pleft = tmp->_pright;delete tmp;}else{tmp_parent->_pright = tmp->_pright;delete tmp;}return true;}}}//未找到key值,pcur指针已经为空,返回false;return false;}//-----------------------bool FindR(const K& key){return _FindR(key,_proot);}//-----------------------bool InsertR(const K&key){return _InsertR(key,_proot);}//-----------------------bool EraseR(const K&key){return _EraseR(key,_proot);}//-----------------------private:/** 私有成员包括成员函数以及成员变量*/Node *_proot = nullptr; //-----------------------protected:void _InOrder(Node *root){if(root == nullptr){return;}_InOrder(root->_pleft);cout<<root->_key<<" ";_InOrder(root->_pright);}//-----------------------bool _FindR(const K&key,Node*root){if(root == nullptr) return false;if(root->_key == key) return true;if(root->_key>key) return _FindR(key,root->_pleft);else  return _FindR(key,root->_pright);}  //-----------------------bool _InsertR(const K&key,Node*& root){//纯高效法/** 该方法重点在于使用了一个引用来引用指针,* 指针既可以为判断条件(为nullptr),* 也不缺乏其为上一个节点的左\右子树;* */if(root == nullptr) {root = new Node(key);return true;}if(root->_key < key){return _InsertR(key,root->_pright);}else if(root->_key>key){return _InsertR(key,root->_pleft);}else return false;}//-----------------------bool _EraseR(const K& key, Node*& root){if(root == nullptr) return false;if(key>root->_key) return _EraseR(key,root->_pright);else if(key<root->_key) return _EraseR(key,root->_pleft);else{/** 在该递归删除中同样利用到了指针引用的方式 // 具体能够指针引用是因为递归每次都会开辟出新的栈帧* 解决了大部分的判断问题(具体表现在左为空或右为空)* 在判断两个子树都不为空的条件下,具体的思路是利用了循环找到可以进行伪删除法的数,再重新调用该子函数完成删除*  其想法可以理解为将问题划分为子问题*  (将左右均不为空的方式在进行一系列操作后将其最终以左为空或者右为空的问题进行解决);* */Node *cur = root;//左为空if(root->_pleft == nullptr){root = root->_pright;delete cur;return true;} //右为空 else if(root->_pright == nullptr){root = root->_pleft;delete cur;return true;}//左右都不为空else{cur = root->_pleft; while(cur->_pright) cur = cur->_pright;root->_key = cur->_key;_EraseR(cur->_key,root->_pleft);return true; }  }}//-----------------------Node* _CopyNode(const Node* node){if(node == nullptr) {return nullptr;}Node*newnode = new Node(node->_key);newnode->_pleft = _CopyNode(node->_pleft);newnode->_pright = _CopyNode(node->_pright);return newnode;}//------------------------void _Destroy(Node*& root){if(root == nullptr) return;_Destroy(root->_pleft);_Destroy(root->_pright);delete root;root = nullptr;}};

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

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

相关文章

v-model和:model的区别

问题 在我们使用Element plus框架时&#xff0c;经常会遇到表单&#xff0c;比如代码块&#xff1a; <el-form ref"ruleFormRef" :model"ruleForm" :rules"rules" label-width"120px" class"demo-ruleForm" :size"…

【umi-reques】umi-request 配置request后,无法覆盖默认设置的 Content-Type:text/plain

https://github.com/ant-design/ant-design-pro/issues/5846 设置mode: ‘no-cors’ 再设置 headers: {Content-Type: application/json;charsetUTF-8}不会生效。 要么让后端加上cors&#xff0c;要么前端通过代理方式访问&#xff0c;如果要改Content-Type&#xff0c; 就不…

C++新经典模板与泛型编程:用成员函数重载实现is_base_of

用成员函数重载实现is_base_of std::is_base_of是一个C 11标准中用于判断某个类是否是另一个类父类的类模板。 #include "killCmake.h"#include<string>using namespace std;class A { };class B : public A { public:B(int x): x_(x){} private:int x_; };/…

使用 GROUP BY 进行数据库分析:以图书销售数据库为例

让我们通过一个简单但实用的例子来理解 GROUP BY 的使用。我们将以一个图书销售数据库为例。这个数据库包含两张表&#xff1a;一张是图书信息表 (books)&#xff0c;另一张是销售记录表 (sales)。我们会先创建这两张表&#xff0c;然后插入一些数据&#xff0c;并展示如何使用…

1320:【例6.2】均分纸牌(Noip2002)

【题目描述】 有n堆纸牌&#xff0c;编号分别为 1&#xff0c;2&#xff0c;…,n 。每堆上有若干张&#xff0c;但纸牌总数必为n 的倍数。可以在任一堆上取若干张纸牌&#xff0c;然后移动。 移牌规则为&#xff1a;在编号为1 的堆上取的纸牌&#xff0c;只能移到编号为 2 的堆…

PHP对接企业微信

前言 最近在做项目中&#xff0c;要求在后台管理中有企业微信管理的相关功能。相关准备工作&#xff0c;需要准备好企业微信账号&#xff0c;添加自建应用&#xff0c;获得相应功能的权限&#xff0c;以及agentid、secre等。 参考文档&#xff1a; 企业微信开发文档 功能实现 因…

指针数组和数组指针作为形式参数

类型 形式参数为指针数组形式参数为数组指针总结 形式参数为指针数组 void test(int* p[3], int n) {printf("p为&#xff1a;%p\n", p);//p为&#xff1a;000000D93D71F718p;printf("p为&#xff1a;%p\n", p);//p为&#xff1a;000000D93D71F720 加了8p…

2、Redis变慢原因排查(下)

感觉Redis变慢了&#xff0c;这些可能的原因你查了没 &#xff1f;(下) Redis变慢排查的上一篇【感觉Redis变慢了&#xff0c;这些可能的原因你查了没 &#xff1f;(上)】&#xff0c;我们是基于Redis命令为入口&#xff0c;比如命令使用不得当&#xff0c;bigkey问题&#xf…

开发短视频矩阵实时直播需要用到哪些技术?

现在越来越多的人或公司都想开发出自己的直播网站或者直播APP&#xff0c;但是在技术这一块又不知道怎么下手&#xff0c;那么我就给大家讲一下在视频直播系统开发中要了解那些知识和技术&#xff1a; 在开发上需要用的技术有&#xff1a; 摄像头采集 音视频编解码 流媒体协议…

⭐Unity 搭建UDP客户端(01) 配合网络调试助手测试

1.接收来自服务器的消息 using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine;public class UDPManager:MonoBehaviour {public string recvStr; //服务器返回值public string UDPClientAddRess "192.168.2.39&q…

uniapp点击图片预览功能?

uni-app点击图片预览功能需要使用uni-app提供的uni.previewImage()方法来实现。具体步骤如下&#xff1a; 在点击事件中&#xff0c;将需要预览的图片URL作为参数传递给uni.previewImage()方法。 在uni.previewImage()方法中&#xff0c;使用current参数指定当前预览的图片URL…

debian11,debian 如何删除虚拟内存,交换分区

1.以管理员身份登录系统 2.输入以下命令以删除虚拟内存,该命令将关闭当前正在使用的虚拟内存。 sudo swapoff -a 3.输入以下命令以永久删除虚拟内存(硬盘内存文件)&#xff1a; sudo rm /swapfile 4.重启系统 总结:以上步骤将删除 Debian 11 中的虚拟内存。请注意&#xf…

数字人对话系统 Linly-Talker

&#x1f525;&#x1f525;&#x1f525;数字人对话系统 Linly-Talker&#x1f525;&#x1f525;&#x1f525; English 简体中文 欢迎大家star我的仓库 https://github.com/Kedreamix/Linly-Talker 2023.12 更新 &#x1f4c6; 用户可以上传任意图片进行对话 介绍 Lin…

OpenDDS之名词概念介绍

无论是自己还是其它朋友在研究opendds源码或者开发手册的时候&#xff0c;往往会朋友一些专有名词&#xff0c;看上去后不明所以&#xff0c;或者对其真实表达的意思不透彻&#xff0c;似是而非的感觉。本文对我自己对这些术语的理解进行解释&#xff0c;希望对大家有所帮助。 …

SolidWorks Simulation 有限元分析-升降架分析

问题描述&#xff1a;一个载重为 1800N 的升降架承受一外部水压柱筒的作用&#xff0c;该水压柱筒与基座上的滑块相连。 1. 打开零件。 2. 我们新建一个静应力分析的算例&#xff0c;如下图所示。 3. 设置材料。我们需要为模型指定模型的材料属性。点击选中模型&#xff0c;鼠…

Excel 分列功能

一. 需求 ⏹有一段文本&#xff0c;文本一共有7列。这7列文本之间的分隔符不相同 有一个空格的有多个空格的有Tab的jmw_state 和 method 之间用 & 连接 现在要求&#xff0c;将这段文本粘贴到Excel中&#xff0c;进行分列。并且需要将 jmw_state 和 method 也进行分列 也…

RabbitMQ(一)概述

1 RabbitMQ 概念 RabbitMQ 是一个消息中间件&#xff1a;它接受并转发消息。你可以把它当做一个快递站点&#xff0c;当你要发送一个包裹时&#xff0c;你把你的包裹放到快递站&#xff0c;快递员最终会把你的快递送到收件人那里&#xff0c;按照这种逻辑 RabbitMQ 是一个快递站…

Linux(16):认识系统服务(daemons)

什么是 daemon 与系统服务 【服务】一般的说明是【常驻在记体体中的程序&#xff0c;且可以提供一些系统或网络功能&#xff0c;那就是服务】。而服务一般的英文说法是【service】。 简单的说&#xff0c;系统为了某些功能必须要提供一些服务(不论是系统本身还是网络方面)&…

python基于ModBusTCP服务端的业务实现特定的client

python实现ModBusTCP协议的client是一件简单的事情&#xff0c;只要通过pymodbus、pyModbusTCP等模块都可以实现&#xff0c;本文采用pymodbus。但要基于ModBusTCP服务端的业务实现特定的client&#xff0c;那得看看服务端是否复杂。前面系列文章&#xff0c;我们学习了对服务端…

C语言基础概念考查备忘 - 标识符、关键字、预定义标识符、语法检查、语义检查 ... 左值、右值、对象、副作用、未定义行为、sizeof是什么等等

什么是标识符、关键字和预定义标识符&#xff1f;三者有何区别&#xff1f; 当谈论C语言中的标识符、关键字和预定义标识符时&#xff0c;让我们从每个概念的基础开始。 标识符&#xff08;Identifiers&#xff09;&#xff1a; 标识符是用来给变量、函数、类型等命名的。在…