C++《二叉搜索树》

在初阶数据结构中我学习了树基础的概念以及了解了顺序结构的二叉树——堆和链式结构二叉树该如何实现,那么接下来我们将进一步的学习二叉树,在此会先后学习到二叉搜索树、AVL树、红黑树;通过这些的学习将让我们更易于理解后面set、map、哈希等的使用以及对底层结构的了解。在此先本篇中我们将了解二次搜索树的概念以及实现二叉搜索树插入、删除等的操作,在了解了这些之后相信在下一篇的set和map的学习你将轻松许多,接下来就开始本篇的学习吧!!!


 1.二叉搜索树的概念

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

• 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值
• 若它的右子树不为空,则右子树上所有结点的值都
大于等于根结点的值
• 它的左右子树也分别为二叉搜索树
• 二叉搜索树中可以支持插入相等的值,也可以不支持插入相等的值,具体看使用场景定义,后续我们学习map/set/multimap/multiset系列容器底层就是二叉搜索树,其中map/set不支持插入相等值,multimap/multiset支持插入相等值

例如以下左边图示的就是不支持插入相等值得二叉搜索树,右边就是支持插入相等值得二叉搜索树

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

最好得情况下,在此在二叉搜索树当中最为完全二叉树(或者接近完全二叉树),其高度为: O(log2 N)

例如以下示例:

最差情况下,在此⼆叉搜索树退化为单支树(或者类似单支),其高度为:
O(\frac{N}{2})

例如以下示例:

那么通过以上对二叉树最好和最坏情况的分析就可以得出综合而言二叉搜索树增删查改时间复杂度为: O(N)

那么通过以上的分析可以看出二叉搜索树这样的效率显然无法满足我们需求的,我们后续需要继续讲解二叉搜索树的变形,平衡二叉搜索树AVL树和红黑树,才能适用于我们在内存中存储和搜索数据。


在此你可能会想到在二叉搜索树中查找数据不就类似与二分查找吗,那么直接使用之前学习的二分查找不就可以了吗,二分查找的效率相比二叉搜索树还更好,那为什么还要学习了解二叉搜索树呢?
在此二分查找也可以实现 O(logN) 级别的查找效率,但是二分查找有两大缺陷:

1. 需要存储在支持下标随机访问的结构中,并且有序。

在使用到二分查找示我们使用的是数据对应的下标来实现查找,这就使得当被查找的一系列数据不是存储在数组里时就需要先将数据都存储在数组当中并且还要将数据排序成升序,在这个过程中就会有时间和空间上的损耗了 

2. 插入和删除数据效率很低,因为存储在下标随机访问的结构中,插入和删除数据⼀般需要挪动数据。

在二分查找当中以上提到的缺点其实还不是最致命的,最要命的是当一组数据已经存储在数组当中时并且已经排序好时,如果之后我们要在这组数据当中再插入新的元素或者是要将原数据中的一个元素删除,那么通过之前顺序表的学习我们就知道每插入或者是删除一个元素时间复杂度都为O(N),若多次进行操作这就使得时间复杂度非常高了

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


通过以上的分析这里也就体现出了平衡二叉搜索树的价值,在插入元素或者是删除元素都相比使用二分查找要有优势。

3. 二叉搜索树的插入

3.1插入分析

在此在二叉搜索树当中插入新的节点就要按照以下步骤进行分析:
1. 树为空,则直接新增结点,赋值给root指针
2. 树不空,按二叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左走,找到空位置,插入新结点。
3. 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑⼀致性,插入相等的值不要⼀会往右走,⼀会往左走

例如以下示例:
假设我们要将以下的数组元素依次插入到二叉搜索树当中

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

 所以元素插入完之后二叉搜索树的形式就如下所示:
 

这时如果我们要再插入值为16的元素在二叉搜索树中过程图就如下所示:

 

 再插入值为3的元素在二叉搜索树中过程图就如下所示:
 

 

3.2 插入代码实现 

在实现二叉搜索树的插入代码之前我们先要来实现二叉搜索树大体的结构代码

在此我们先创建一个BSTree.h的头文件在该文件当中来实现二叉搜索树的结构以及各个功能,再创建一个test.cpp的文件用于测试我们实现的二叉搜索树的各个功能是否能满足要求

实现了文件的创建之后接下来就来实现二叉搜索树的大体结构。在此由于二叉搜索树是由各个节点构成的,那么和之前实现链式结构的二叉树一样先要实现表示节点的结构体

 

#include<iostream>
using namespace std;template<class K>
struct BSTreeNode
{K _key;BSTreeNode<K>* left;BSTreeNode<K>* right;BSTreeNode(const K& key):_key(key),left(nullptr),right(nullptr){}
};

在此就创建一个结构体BSTreeNode来表示二叉树内节点,在节点当中有三个变量分别是_key表示节点内的数据、_left存储该节点左孩子节点的指针、_right存储该节点右孩子节点的指针。并且将该结构体实现成模板这样就可以支持节点内的元素是任意类型的数据,还有在结构体当中还实现了构造函数。

实现了节点的结构体之后接下来就可以来实现表示二叉搜索树的类,在此我们将类命名为BSTree,实现的是模板类,实现的大体结构如下所示:

template<class K>
class BSTree
{typedef BSTreeNode<K> Node;public://使用编译器生成的默认构造函数BSTree() = default;//实现各种功能的成员函数……private://头节点Node* _root = nullptr;};

完成了以上操作接下来就可以来实现插入函数的代码了

注:以下实现的插入函数是数据不支持冗余的情况也就是二叉树当中不支持插入相等的值

bool Insert(const K& key)
{//当根节点为空时if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;//节点的父节点Node* parentcur = nullptr;while (cur){//当key小于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}当key大于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key等于当前节点的值else{return false;}}cur = new Node(key);//当新节点内的值大于父节点内的值时if (parentcur->_key  <  key){parentcur->right = cur;}//当新节点内的值小于父节点内的值时else{parentcur->left = cur;}return true;}

4. 二叉搜索树的查找

4.1查找分析

在二叉搜索树中查找节点就要按照以下步骤进行分析:

1. 从根开始比较,查找x,x比根的值大则往右边走查找,x比根值小则往左边走查找。
2. 最多查找高度次,走到到空,还没找到,这个值不存在。
3. 如果不支持插入相等的值,找到x即可返回
4. 如果支持持插入相等的值,意味着有多个x存在,⼀般要求查找中序的第⼀个x。

例如以下示例: 

当要查找3时,要找到1的右孩子的那个3值返回 

 

4.2 查找代码实现 

在进行了二叉搜索树的节点查找的分析之后接下来我们就来实现查找代码

注:以下实现的查找函数是数据不支持冗余的情况也就是二叉树当中不支持插入相等的值 

Node* Find(const K& key)
{//当根结点为空时if (_root == nullptr){return nullptr;}Node* cur = _root;Node* parentcur = nullptr;while (cur){//当key大于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}//当key小于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key等于当前节点的值else{return cur;}}//当前二叉树中找不到值为key的节点return nullptr;
}

5. 二叉搜索树的删除 

5.1 删除分析

在二叉搜索树当中节点的删除相比插入和查找就复杂一些了,在此会出现以下的多种情况接下来就来一一分析

在此要删除的节点会有以下的四种情况:

1.要删除结点N左右孩子均为空

当要删除节点的节点左右孩子节点都为空时就需要把N结点的父亲对应孩子指针指向空,直接删除N结点

例如以下示例:

在以上二叉搜索树当中要删除值为1的节点就需要将节点1删除之后再将其父节点的左孩子节点指向空

2. 要删除的结点N左孩子位空,右孩子结点不为空

当要删除节点的节点左孩子节点为空时就需要把N结点的父亲对应孩子指针指向N的右孩子,之后直接删除N结点

例如以下示例:


 

在以上二叉搜索树当中要删除值为10的节点就需要将其父节点的左孩子变为原节点的右孩子节点之后再将节点10删除

 3.要删除的结点N右孩子位空,左孩子结点不为空

当要删除节点的节点右孩子节点为空时就需要把N结点的父亲对应孩子指针指向N的左孩子,之后直接删除N结点

例如以下示例:

 

在以上二叉搜索树当中要删除值为14的节点就需要将其父节点的左孩子变为原节点的左孩子节点之后再将节点14删除

4. 要删除的结点N左右孩子结点均不为空

当要删除的节点左右孩子节点都不为空时,这是就不能像以上的情况一样简单的改变要删除节点的父节点指针,这时由于无法直接删除N结点,这是因为N的两个孩⼦无处安放,只能用替换法删除。在此根据二叉搜索树的性质就需要找N左子树的值最大结点R(最右结点)或者N右子树的值最小结点R(最左结点)替代N,因为这两个结点中任意⼀个,放到N的位置,都满足⼆叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转而变成删除R结点,R结点符合情况2或情况3,可以直接删除。 

例如以下示例:
 

在以上二叉搜索树当中要删除节点值为8的节点和值为3的节点,由于这两个节点都是左右孩子节点都不为空的节点,因此要删除值为8的节点就需要找到其左子树最右的节点或者是右子树最左的节点(在此我们是找右子树最左的节点)

之后交换要删除的节点和找出的节点的值

 

最后删除交换之后值为8的节点即可

 

注:要删除值为3的节点和以上的方式也类型在此就不再细致的讲解

 

5.2 删除代码实现

bool Erase(const K& key)
{//当根节点为空时if (_root == nullptr){return false;}//当前节点Node* cur = _root;//当前节点的父节点Node* parentcur = _root;while (cur){//当key的值大于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}//当key的值大于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key的值等于当前节点的值else{//当要删除的节点左孩子节点为空if (cur->left==nullptr){//当要删除的节点为根结点时直接改变_root指针的指向,之后再将原根结点释放if (cur == _root){Node* Right = cur->right;delete cur;_root = Right;}else{//当要删除的节点cur是其父节点的左节点时if (cur == parentcur->left){parentcur->left = cur->right;delete cur;}//当要删除的节点cur是其父节点的右节点时else if (cur == parentcur->right){parentcur->right = cur->right;delete cur;}}return true;}//当要删除的节点右孩子节点为空else if (cur->right==nullptr){//当要删除的节点为根结点时直接改变_root指针的指向,之后再将原根结点释放if (cur == _root){Node* Left = cur->left;delete cur;_root = Left;}else{//当要删除的节点cur是其父节点的左节点时if (cur == parentcur->left){parentcur->left = cur->left;delete cur;}//当要删除的节点cur是其父节点的右节点时else if (cur == parentcur->right){parentcur->right = cur->left;delete cur;}}return true;}//当要删除的节点左右孩子节点都为空else{Node* minrightParent = cur;Node* minright = cur->right;//找出当前节点右子树中的最左节点while (minright->left){minrightParent = minright;minright = minright->left;}cur->_key = minright->_key;//当最左节点为其父节点的左节点时if (minrightParent->left == minright){minrightParent->left = minright->right;}//当最左节点为其父节点的右节点时else {minrightParent->right = minright->right;}delete minright;return true;	}}}//当找不到要删除的节点就返回falsereturn false;}

 6. 二叉搜索树遍历

由于二叉搜索树的性质一棵二叉搜索树中序遍历输出的结果就是递增的,那么接下来我们就试着来实现中序遍历的代码

void Inorder()
{_Inorder(_root);cout << endl;
}void _Inorder(Node* root)
{if (root == nullptr){return;}_Inorder(root->left);cout << root->_key <<" ";_Inorder(root->right);
}

在此我们实现的中序遍历代码如上所示,那么这时你可能就会有疑问了,为什么要实现两个中序遍历的函数,不是直接使用一个函数就可以满足要求了吗?

在此要考虑到的是在BsTree类以外用户是无法得到二叉搜索树的根节点的,但是在调用中序遍历的函数根据之前我们使用递归的方式是需要一开始就需要将二叉树的根结点作为中序遍历函数的参数的。因此为了解决该问题就再在BSTree类内实现一个函数来调用中序遍历的成员函数,由于是在类的内部在此是可以访问私有的成员变量的,在类外部用户要使用中序遍历时就只需要调用无参的成员函数Inorder就可以得到二叉搜索树中序遍历的结果了。

7. 二叉搜索树完整代码

#include<iostream>
using namespace std;template<class K>
struct BSTreeNode
{K _key;BSTreeNode<K>* left;BSTreeNode<K>* right;BSTreeNode(const K& key):_key(key), left(nullptr), right(nullptr){}
};template<class K>
class BSTree
{typedef BSTreeNode<K> Node;public://使用编译器生成的默认构造函数BSTree() = default;bool Insert(const K& key){//当根节点为空时if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;//节点的父节点Node* parentcur = nullptr;while (cur){//当key小于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}当key大于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key等于当前节点的值else{return false;}}cur = new Node(key);//当新节点内的值大于父节点内的值时if (parentcur->_key < key){parentcur->right = cur;}//当新节点内的值小于父节点内的值时else{parentcur->left = cur;}return true;}Node* Find(const K& key){//当根结点为空时if (_root == nullptr){return nullptr;}Node* cur = _root;Node* parentcur = nullptr;while (cur){//当key大于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}//当key小于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key等于当前节点的值else{return cur;}}//当前二叉树中找不到值为key的节点return nullptr;}bool Erase(const K& key){//当根节点为空时if (_root == nullptr){return false;}//当前节点Node* cur = _root;//当前节点的父节点Node* parentcur = _root;while (cur){//当key的值大于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}//当key的值大于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key的值等于当前节点的值else{//当要删除的节点左孩子节点为空if (cur->left == nullptr){//当要删除的节点为根结点时直接改变_root指针的指向,之后再将原根结点释放if (cur == _root){Node* Right = cur->right;delete cur;_root = Right;}else{//当要删除的节点cur是其父节点的左节点时if (cur == parentcur->left){parentcur->left = cur->right;delete cur;}//当要删除的节点cur是其父节点的右节点时else if (cur == parentcur->right){parentcur->right = cur->right;delete cur;}}return true;}//当要删除的节点右孩子节点为空else if (cur->right == nullptr){//当要删除的节点为根结点时直接改变_root指针的指向,之后再将原根结点释放if (cur == _root){Node* Left = cur->left;delete cur;_root = Left;}else{//当要删除的节点cur是其父节点的左节点时if (cur == parentcur->left){parentcur->left = cur->left;delete cur;}//当要删除的节点cur是其父节点的右节点时else if (cur == parentcur->right){parentcur->right = cur->left;delete cur;}}return true;}//当要删除的节点左右孩子节点都为空else{Node* minrightParent = cur;Node* minright = cur->right;//找出当前节点右子树中的最左节点while (minright->left){minrightParent = minright;minright = minright->left;}cur->_key = minright->_key;//当最左节点为其父节点的左节点时if (minrightParent->left == minright){minrightParent->left = minright->right;}//当最左节点为其父节点的右节点时else{minrightParent->right = minright->right;}delete minright;return true;}}}//当找不到要删除的节点就返回falsereturn false;}void Inorder(){_Inorder(_root);cout << endl;}private://头节点Node* _root = nullptr;void _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->left);cout << root->_key << " ";_Inorder(root->right);}};

8.  二叉搜索树key和key/value使用场景 

8.1 key搜索场景

key搜索场景的形式如下所示:

只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。

在以上我们了解实现的二叉搜索树其实是适用于key的场景,那么接下来就来看看那么场景是属于key的场景

场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位的业主的车牌号录入后台系统,⻋辆进入时扫描⻋牌在不在系统中,在则抬杆,不在则提示非本小区⻋辆,无法进入。

 

场景2:检查⼀篇英文文章单词拼写是否正确,将词库中所有单词放入二叉搜索树,读取文章中的单词,查找是否在二叉搜索树中,不在则波浪线标红提示。 

8.2 key/value搜索场景

key/value搜索场景的形式如下所示:

每⼀个关键码key,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字走二叉搜索树的规则进行比较,可以快速查找到key对应的value。key/value的搜索场景实现的二叉树搜索树支持修改,但是不支持修改key,修改key破坏搜索树结构了,可以修改value。

接下来就来将以上我们实现的key二叉搜索树修改为key/value⼆叉搜索树代码,实现代码如下所示:

#include<iostream>
using namespace std;template<class K,class V>
struct BSTreeNode
{K _key;V _value;BSTreeNode<K ,V>* left;BSTreeNode<K ,V>* right;BSTreeNode(const K& key, const V& value):_key(key);_value(value), left(nullptr), right(nullptr){}
};template<class K,class V>
class BSTree
{typedef BSTreeNode<K,V> Node;public://使用编译器生成的默认构造函数BSTree() = default;bool Insert(const K& key, const V& value){//当根节点为空时if (_root == nullptr){_root = new Node(key,value);return true;}Node* cur = _root;//节点的父节点Node* parentcur = nullptr;while (cur){//当key小于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}//当key大于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key等于当前节点的值else{return false;}}cur = new Node(key, value);//当新节点内的值大于父节点内的值时if (parentcur->_key < key){parentcur->right = cur;}//当新节点内的值小于父节点内的值时else{parentcur->left = cur;}return true;}Node* Find(const K& key){//当根结点为空时if (_root == nullptr){return nullptr;}Node* cur = _root;Node* parentcur = nullptr;while (cur){//当key大于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}//当key小于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key等于当前节点的值else{return cur;}}//当前二叉树中找不到值为key的节点return nullptr;}bool Erase(const K& key){//当根节点为空时if (_root == nullptr){return false;}//当前节点Node* cur = _root;//当前节点的父节点Node* parentcur = _root;while (cur){//当key的值大于当前节点的值if (cur->_key < key){parentcur = cur;cur = cur->right;}//当key的值大于当前节点的值else if (cur->_key > key){parentcur = cur;cur = cur->left;}//当key的值等于当前节点的值else{//当要删除的节点左孩子节点为空if (cur->left == nullptr){//当要删除的节点为根结点时直接改变_root指针的指向,之后再将原根结点释放if (cur == _root){Node* Right = cur->right;delete cur;_root = Right;}else{//当要删除的节点cur是其父节点的左节点时if (cur == parentcur->left){parentcur->left = cur->right;delete cur;}//当要删除的节点cur是其父节点的右节点时else if (cur == parentcur->right){parentcur->right = cur->right;delete cur;}}return true;}//当要删除的节点右孩子节点为空else if (cur->right == nullptr){//当要删除的节点为根结点时直接改变_root指针的指向,之后再将原根结点释放if (cur == _root){Node* Left = cur->left;delete cur;_root = Left;}else{//当要删除的节点cur是其父节点的左节点时if (cur == parentcur->left){parentcur->left = cur->left;delete cur;}//当要删除的节点cur是其父节点的右节点时else if (cur == parentcur->right){parentcur->right = cur->left;delete cur;}}return true;}//当要删除的节点左右孩子节点都为空else{Node* minrightParent = cur;Node* minright = cur->right;//找出当前节点右子树中的最左节点while (minright->left){minrightParent = minright;minright = minright->left;}cur->_key = minright->_key;//当最左节点为其父节点的左节点时if (minrightParent->left == minright){minrightParent->left = minright->right;}//当最左节点为其父节点的右节点时else{minrightParent->right = minright->right;}delete minright;return true;}}}//当找不到要删除的节点就返回falsereturn false;}void Inorder(){_Inorder(_root);cout << endl;}private://头节点Node* _root = nullptr;void _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->left);cout << root->_key << ":" << root->_val << " ";_Inorder(root->right);}};


 

在以上我们了解实现的二叉搜索树其实是适用于key的场景,那么接下来就来看看那么场景是属于key的场景

场景1:商场无人值守车库,入⼝进场时扫描⻋牌,记录车牌和入场时间,出口离场时,扫描牌,查找入场时间,用当前时间-⼊场时间计算出停⻋时长,计算出停车费用,缴费后抬杆,车辆离场。

 

场景1:简单中英互译字典,树的结构中(结点)存储key(文)和vlaue(中文),搜索时输⼊英文,则同时查找到了英文对应的中文。
场景3:统计⼀篇文章中单词出现的次数,读取⼀个单词,查找单词是否存在,不存在这个说明第⼀次出现,(单词,1),单词存在,则++单词对应的次数。

以下的两个简单的示例就是使用二叉搜索树的key/val来解决
 

#include"BSTree.h"int main()
{//示例1
//将以下英文单词和对应的中文翻译绑定到一起,当用户输入输入正确的单词就输出其中文意思,否则就输出单词拼写错误BSTree<string, string> dict;dict.Insert("insert", "插入");dict.Insert("erase", "删除");dict.Insert("left", "左边");dict.Insert("string", "字符串");string str;while (cin >> str){auto ret = dict.Find(str);if (ret){cout << str << ":" << ret->_val << endl;}else{cout << "单词拼写错误" << endl;}}//示例2
//统计字符串数组当中各个水果的出现次数string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };// 统计水果出现的次BSTree<string, int> countTree;for (auto str : strs){auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);}else{ret->_val++;}}countTree.Inorder();return 0;
}

以上就是本篇的区别内容了,希望能得到你的点赞和收藏 ❤️

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

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

相关文章

Apollo9.0源码部署(Nvidia显卡)

本文参照Apollo官方部署例程&#xff0c;进行修改。解决在部署期间遇到的网络不通、编译卡死、编译失败等问题。&#xff08;安装具有时效性&#xff0c;仅供参考&#xff09; 步骤1. 安装docker,显卡驱动、nvidia插件&#xff0c;此步骤可见专栏第一、二 节 步骤2. 拉取代…

第02章_MySQL环境搭建(基础)

1. MySQL 的卸载 1.1 步骤1&#xff1a;停止 MySQL 服务 在卸载之前&#xff0c;先停止 MySQL8.0 的服务。按键盘上的 “Ctrl Alt Delete” 组合键&#xff0c;打开“任务管理器”对话 框&#xff0c;可以在“服务”列表找到“MySQL8.0” 的服务&#xff0c;如果现在“正在…

【华为】配置VXLAN构建虚拟网络实现相同网段互通(静态方式)

微思网络 厦门微思网络 组网需求 企业已经建成比较成熟的园区网络&#xff0c;但是没有专用的数据中心网络&#xff0c;所有的服务器分布在不同的部门&#xff0c;并且不具备集中放置的条件。现在用户希望在已有园区网络上构建一个虚拟网络&#xff0c;需求如下&#xff1a; 将…

linux系统运维面试题(二)(Linux System Operations Interview Questions II)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

互联网直播/点播EasyDSS视频推拉流平台视频点播有哪些技术特点?

在数字化时代&#xff0c;视频点播应用已经成为我们生活中不可或缺的一部分。监控技术与视频点播的结合正悄然改变着我们获取和享受媒体内容的方式。这一变革不仅体现在技术层面的进步&#xff0c;更深刻地影响了我们。 EasyDSS视频直播点播平台是一款高性能流媒体服务软件。E…

最新SQL Server 2022保姆级安装教程【附安装包】

目录 一、安装包下载&#xff1a; 下载链接&#xff1a;https://pan.quark.cn/s/b1c0c63d61ec 二、安装SQL Server 1.下载安装包后解压出来&#xff0c;双击打开 2.等待加载安装程序 3.点击基本安装 4.点击接受 5.点击浏览 6.在D盘新建文件夹 7.命名为【Sql Server】&…

香港大带宽服务器:助力高效网络应用

随着全球化的加速和互联网流量的持续增长&#xff0c;大带宽服务器逐渐成为企业在全球范围内运营的关键设施。香港凭借其优越的地理位置、先进的网络基础设施和政策环境&#xff0c;成为部署大带宽服务器的重要节点之一。本文将全面探讨香港大带宽服务器的核心优势、应用场景及…

设计模式:责任链实现数据流风格的数据处理

数据流风格 数据流风格是软件架构中的一种风格&#xff0c;主要是面向数据&#xff0c;用于进行流式的数据处理&#xff1b;数据流风格的代表有管道-过滤器风格和批处理序列风格&#xff0c;这里主要是指管道-过滤器风格。 管道-过滤器风格就像其名字一样&#xff0c;是以一个…

PGSQL物化视图(Materialized View)

在 PostgreSQL 中&#xff0c;物化视图&#xff08;Materialized View&#xff09;是一种特殊的数据库对象&#xff0c;它存储了查询的结果集&#xff0c;并可以定期刷新以反映基础表中的数据变化。物化视图可以提高查询性能&#xff0c;因为它减少了每次查询时重新计算数据的需…

浏览器缓存与协商缓存

1. 强缓存&#xff08;Strong Cache&#xff09; 定义 强缓存是指在缓存的资源有效期内&#xff0c;浏览器会直接使用缓存中的数据&#xff0c;而不会发起网络请求。也就是说&#xff0c;浏览器会直接从本地缓存读取资源&#xff0c;不会与服务器进行任何交互。 如何控制强缓…

JS听到了替罪的回响

这篇还是继续写JS 这是有关函数的一些内容 函数 为什么需要函数 函数是被设计为执行指定任务的代码块 函数可以把具有相同或者相似逻辑的代码包裹起来&#xff0c;通过函数调用执行这些被包裹的代码逻辑&#xff0c;这样的优势是有利于精简代码方便复用 函数使用 这是函…

【优选算法】前缀和

目录 一、[【模板】前缀和](https://www.nowcoder.com/practice/acead2f4c28c401889915da98ecdc6bf?tpId230&tqId2021480&ru/exam/oj&qru/ta/dynamic-programming/question-ranking&sourceUrl%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595…

SAP ME2L/ME2M/ME3M报表增强添加字段

SAP ME2L/ME2M/ME3M报表增强添加字段&#xff08;包含&#xff1a;LMEREPI02、SE18:ES_BADI_ME_REPORTING&#xff09; ME2L、ME2M、ME3M这三个报表的字段增强&#xff0c;核心点都在同一个结构里 SE11:MEREP_OUTTAB_PURCHDOC 在这里加字段&#xff0c;如果要加的字段是EKKO、…

破解天然气巡检挑战,构建智能运维体系

一、行业现状 天然气行业在能源领域地位举足轻重&#xff0c;其工作环境高风险&#xff0c;存在有毒有害、易爆气体及高温等情况&#xff0c;且需持续监控设备运行状态&#xff0c;人工巡检面临巨大挑战与风险。好在随着科技发展&#xff0c;防爆巡检机器人的应用为天然气管道…

TSmaster CAN/CANFD 诊断(Diagnostic_CAN)

文章目录 1、Diagnostic TP 参数配置1.1 传输层参数&#xff1a;1.2 服务层参数1.3 Seed&Key 2、基础诊断配置2.1 添加/删除 服务2.2 配置 BasicDiagnostic 服务参数 3、诊断控制台4、自动诊断流程4.1 流程用例管理4.2 配置诊断流程&#xff08;UDS Flow&#xff09;4.2.1 …

详解Servlet的使用

目录 Servlet 定义 动态页面 vs 静态页面 主要功能 Servlet的使用 创建Maven项目 引入依赖 创建目录 编写代码 打war包 部署程序 验证程序 Smart Tomcat 安装Smart Tomcat 配置Smart Tomcat插件 启动Tomcat 访问页面 路径对应关系 Servlet运行原理 Tomcat的…

mysql数据库双机互为主从设置与数据库断电无法启动处理

一、mysql数据库双机互为主从设置 前言 1.环境windows 2.数据库8.0 3.服务器1&#xff1a;192.168.12.1 4.服务器2&#xff1a;192.168.12.2 1. 设置数据库的配置文件 对文件名&#xff1a;my.ini进行修改 服务器1&#xff1a;192.168.12.1配置文件设置 [mysql] 下添加如…

strupr(arr);模拟实现(c基础)

hi , I am 36 适合对象c语言初学者 strupr(arr);函数是把arr数组变为大写字母&#xff0c;并返回arr 介绍一下strupr(arr)&#xff1b;(c基础&#xff09;-CSDN博客 现在进行My__strupr(arr);模拟实现 #include<stdio.h>//My__strupr(arr); //返回值为arr(地址),于是…

项目实战:基于深度学习的人脸表情识别系统设计与实现

大家好&#xff0c;人脸表情识别是计算机视觉领域中的一个重要研究方向&#xff0c;它涉及到对人类情感状态的理解和分析。随着深度学习技术的发展&#xff0c;基于深度学习的人脸表情识别系统因其高精度和强大的特征学习能力而受到广泛关注。本文旨在探讨基于深度学习的人脸表…

架构师思维中的人、产品和技术

架构思维主要是一种以产品和业务为驱动的顶层解决问题的思维,需要同时考虑产品、人和技术3重关系,思维点需要同时落在三维体系中。虽然架构师很多时候做的工作其实只是分和合,即所谓的系统分拆及重新组合,但综合能力要求很高,需要同时具备思维的高度和深度,在思维抽象的同…