【C++】模拟实现二叉搜索(排序)树

🦄个人主页:修修修也

🎏所属专栏:实战项目集

⚙️操作环境:Visual Studio 2022


目录

一.了解项目功能

二.逐步实现项目功能模块及其逻辑详解

📌实现BSTreeNode类模板

🎏构造BSTreeNode类成员变量

🎏实现BSTreeNode类构造函数

📌实现BSTree类模板

🎏构造BSTree类成员变量

🎏实现BSTree类构造函数

🎏实现BSTree类InOrder()函数

🎏实现BSTree类Find()函数

🕹️循环版Find()函数

🕹️递归版FindR()函数

🎏实现BSTree类Insert()函数

🕹️循环版Insert()函数

🕹️递归版InsertR()函数

🎏实现BSTree类Erase()函数

🕹️循环版Erase()函数

🕹️递归版EraseR()函数

🎏实现BSTree类析构函数

🎏实现BSTree类拷贝构造函数

🎏实现BSTree类赋值运算符重载operator=函数

三.项目完整代码

📌test.cpp文件

📌BinarySearchTree.h文件

结语


一.了解项目功能

        在本次项目中我们的目标是实现一个二叉搜索树(Binary Search Tree)类模板,还不了解二叉搜索树概念的朋友可以先移步[【数据结构】什么是二叉搜索(排序)树?] 其结构图示如下:

        二叉搜索树结点(BSTreeNode)需要包含三个要素:键值_key,左指针域_left,右指针域_right.结点(BSTreeNode)逻辑结构图示如下:

        二叉搜索树需要实现的功能有:

  1. 构造函数
  2. 中序遍历InOrder()函数
  3. 查找函数Find()--循环版 + FindR()--递归版
  4. 插入函数Insert()--循环版 + InsertR()--递归版
  5. 删除函数Erase()--循环版 + EraseR()--递归版
  6. 析构函数
  7. 拷贝构造函数
  8. 赋值运算符重载operator=函数

二.逐步实现项目功能模块及其逻辑详解

通过第二部分对项目功能的介绍,我们已经对  的功能有了大致的了解,虽然看似需要实现的功能很多,貌似一时间不知该如何下手,但我们可以分步分模块来分析这个项目的流程,最后再将各部分进行整合,所以大家不用担心,跟着我一步一步分析吧!


!!!注意,该部分的代码只是为了详细介绍某一部分的项目实现逻辑,故可能会删减一些与该部分不相关的代码以便大家理解,需要查看或拷贝完整详细代码的朋友可以移步本文第四部分。


📌实现BSTreeNode类模板

🎏构造BSTreeNode类成员变量

        我们在一开始需求分析时就已经明确了BSTreeNode类的成员变量组成包含三个要素:键值_key,左指针域_left,右指针域_right.结点(BSTreeNode),其逻辑结构图示如下:

        这里还有一个小的点需要提一下,我们在这里实现的BSTreeNode类,后续是要给BSTree类使用的,考虑到后续我们在二叉搜索树的操作函数中会有直接操作结点成员变量的情况,如:

root = root->_right; //相当于在BSTreeNode外部直接通过类指针访问了类成员变量_right

        基于class的封装特性,class的成员变量一般都是默认为私有的,如果我们要允许其他类直接访问class的成员变量和函数,就要将其都设置为public,或者通过友元/内部类来解决成员访问问题.

        既然如此,我们不妨直接使用struct定义结点成员变量和函数,因为struct定义的类的成员变量和函数默认就是公有的,完全可以满足我们的需求.

        综上所属,该部分代码如下:

template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;
};

🎏实现BSTreeNode类构造函数

        BSTreeNode的构造函数我们实现两个即可,一个是有参构造,一个是无参构造,而无参构造又可以通过给缺省值的方式和有参构造合二为一,所以我们用初始化列表来实现一下BSTreeNode的构造函数:

//缺省值的作用是在无参调用时直接去调用模板实例化的类的无参构造函数
//这里一定不能将缺省值给0/nullptr!因为你不知道模板实例化的类具体到底是内置类型还是自定义类型(如Date)
//所以要显示调用K类型它自己的无参构造函数
BSTreeNode(const K& key = K()):_left(nullptr),_right(nullptr),_key(key)
{}

📌实现BSTree类模板

🎏构造BSTree类成员变量

        BSTree类成员变量比较简单,就是一个根节点的指针,为了简化模板中出现的BSTreeNode<K>类型的名字,我们将其简化命名为:Node

        该部分实现代码如下:

template<class K>
class BSTree
{typedef BSTreeNode<K> Node;  //简化命名
public://成员函数private://成员变量or成员函数子函数Node* _root;
};

🎏实现BSTree类构造函数

         BSTree类的构造函数非常简单,因为只有一个成员变量根节点指针_root,所以我们用初始化列表将该指针初始话为nullptr即可,代码如下:

BSTree():_root(nullptr)
{}

🎏实现BSTree类InOrder()函数

         BSTree类的中序遍历逻辑和二叉树的中序遍历逻辑一模一样,即

  1. 先递归访问左子树
  2. 再访问根节点
  3. 最后递归访问右子树

        但在面向对象设计中,我们设计类内的递归函数会遇到一个问题,就是类对象调用其成员函数的递归传参问题:

        我们知道C语言完成中序遍历函数时一个参数是恰好可以满足首次调用和递归调用时传参的形式的:

        但是在面向对象语言中,这个参数是不好处理的,因为首次调用时不需要传参,类对象调用成员函数会默认传this指针,所以不需要传参数,但后续递归调用又需要传参数,这就导致这个函数参数有也不对,没有也不对:

        所以好的解决方式是将递归主体封装成子函数,代替成员函数进行递归操作,这种方式我们在后续完成递归成员函数时会一直用到,大家一定要习惯。

        综上所述,该部分实现代码如下:

//中序遍历函数
void InOrder()
{_InOrder(_root);  //代替成员函数完成递归cout<<endl;       //方便后续观察测试用例
}//中序遍历子递归函数
void _InOrder(Node* root)
{if (root == nullptr){return;}_InOrder(root->_left);       //递归访问左子树cout << root->_key << " ";   //访问根节点_InOrder(root->_right);      //递归访问右子树
}

🎏实现BSTree类Find()函数

        BSTree类的查找函数实现思路如下:

  1. 从根节点开始比较查找, 如果查找值比根结点值大,则往右子树继续查找; 如果查找值比根结点值小,则往左子树继续查找;
  2. 最多查找高度次, 即查找到叶子节点, 如果还没找到, 则该值不存在于此树中。

        思路图示如下:


🕹️循环版Find()函数

        循环版Find()函数主要就是借助cur指针变量在二叉搜索树中一直查找,直到找到key值或者找到nullptr值为止,前者代表找到了,后者表示没有找到.

        综上,实现代码如下:

//循环版Find
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;
}

🕹️递归版FindR()函数

        递归版FindR()函数思路主要是根据key值递归去根的左或右子树继续查找,如果找到nullptr值,代表没有找到,就递归回false,如果找到了key值,就递归回true.

        综上,实现代码如下:

//递归FindR成员函数
bool FindR(const K& key)
{return _FindR(_root, key);
}//FindR()函数子递归
bool _FindR(Node* root, const K& key)
{if (root == nullptr){return false;}if (root->_key < key){_FindR(root->_right, key);   //根小于key,递归查找右子树}else if (root->_key > key){_FindR(root->_left, key);    //根大于key,递归查找左子树}else{return true;          //根等于key,表示找到了}
}

🎏实现BSTree类Insert()函数

        BSTree类的插入函数实现思路如下:

  1. 树为空,则直接新增节点,赋值给根节点指针
  2. 树不为空,则按二叉搜索树性质查找插入位置, 在查找到为空的位置插入新增值, 如果查找到该值已存在, 则返回查找失败(二叉搜索树不允许插入重复值)

        思路图示如下:


🕹️循环版Insert()函数

        循环版Insert()函数思路就是创建cur找插入位置,然后parent记录cur的父亲结点,方便找到后的插入工作,逻辑不是很难,细节要注意,实现代码及注释如下:

bool Insert(const K& key)
{ //根节点为空直接令_root等于新插结点if (_root == nullptr){_root = new Node(key);return true;}//开始在子树里面找插入位置cur,同时创建parent记录cur的父节点Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key)    //查找位置key值比新插key值小,向右继续找插入位置{parent = cur;cur = cur->_right;}else if (cur->_key > key)   //查找位置key值比新插key值大,向左继续找插入位置{parent = cur;cur = cur->_left;}else            //查找位置key值和新插key值相等,表明新插key值已经存在于树中了{return false;    //就不用再插入,直接返回插入失败}}//运行到此表示找到插入位置了,下面创建新结点,然后将其链接父节点即可cur = new Node(key);if (parent->_key < key)    //判断新结点和父节点的大小,大于就链接父节点的右指针{parent->_right = cur;}else         //小于就链接父节点的左指针{parent->_left = cur;}return true;
}

🕹️递归版InsertR()函数

        递归InsertR()函数的逻辑是,如果新插key值大于根节点key值,就递归右子树继续插入,如果新插key值小于根节点key值,就递归左子树继续插入,如果相等就返回插入失败.

        逻辑很简单,代码如下:

bool InsertR(const K& key)
{return _InsertR(_root, key);
}bool _InsertR(Node*& root,const K& key)
{if (root == nullptr)    //找到nullptr表示找到插入位置了,直接插入即可{root = new Node(key);//因为子递归的参数是引用,所以可以直接将找到的空位置改为新结点return true;}if (root->_key < key)        //找到的位置key值比新插key值小,往右继续找位置{_InsertR(root->_right, key);}else if (root->_key > key)    //找到的位置key值比新插key值大,往左继续找位置{_InsertR(root->_left, key);}else        //找到的位置key值和新插入key值相等,返回插入失败{return false;}
}

🎏实现BSTree类Erase()函数

        BSTree类的删除函数实现思路如下:

        查找元素是否在二叉搜索中,如果不存在,则返回,如果存在,则待删除结点可能存在以下四种情况:

  1. 待删除结点无孩子结点
  2. 待删除结点只有左孩子结点(不管右孩子)
  3. 待删除结点只有右孩子结点(不管左孩子)
  4. 待删除结点左,右孩子结点都有

        在实际删除操作中,可以将1和2 / 3合并起来,因为我们的逻辑是哪边有孩子就管理,没有孩子就可以不管,因此无孩子结点既可以和不管右孩子结点情况合并又可以和不管左孩子情况合并,合并后实际的删除情况及过程如下三种:

  1. 删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
  2. 删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
  3. 在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除


🕹️循环版Erase()函数

        循环版Erase()函数实现如下,具体细节见代码注释:

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->_left == nullptr)   //左空托右孤,注意左右都空也因为满足左空走这里{if (cur == _root)     //是根节点就没有父节点,直接让右孩子做根节点{_root = cur->_right;}else{if (parent->_right == cur)   //是父的右孩子就托孤到父亲的右指针{parent->_right = cur->_right;}else        //是父的左孩子就托孤到父亲的左指针{parent->_left = cur->_right;}}}else if (cur->_right == nullptr)    //右空托左孤{if (cur == _root)    //是根节点就没有父节点,直接让左孩子做根节点{_root = cur->_left;}else{if (parent->_right == cur)    //是父的右孩子就托孤到父亲的右指针{parent->_right = cur->_left;}else         //是父的左孩子就托孤到父亲的左指针{parent->_left = cur->_left;}}}else        //左右都不为空{//找左树的最右值Node* parent = cur;  //这里不能写=nullptr,防止删的节点的leftMax就是自己的左子树的情况,这样就不会进下面的while循环,parent没有被赋值,就会导致在9行后的if判断里出现对空指针解引用Node* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(cur->_key, leftMax->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur = leftMax;}//走到这里表示待删结点的孩子已经处理好了,可以直接释放了delete cur;return true;}}return false;
}
🕹️递归版EraseR()函数

        递归版EraseR()函数实现如下,具体细节见代码注释:

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)    //小了,向右继续递归删{_EraseR(root->_right, key);}else if (root->_key > key)    //大了,向左继续递归删{_EraseR(root->_left, key);}else        //找到了,开始按三种情况删{Node*  del = root; //因为我们后面会更改root,防止delete时找不到待删元素,先记录下if (root->_left == nullptr)    //左空托右孤{root = root->_right;    //因为是引用,所以可以直接将自己改为右孩子}else if (root->_right == nullptr)    //右空托左孤{root = root->_left;    //因为是引用,所以可以直接将自己改为左孩子}else        //都有找接班{//找替代结点(左子树的最右值)Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}//交换自己和接班的位置swap(root->_key, leftMax->_key);//然后复用递归删除自己就行,但是注意,因为自己和左子树的最右值,//即比自己小一点点的值交换了位置,那么再找自己删自己的时候就不能//从根节点开始找,因为对新根来说自己比它大一点那应该在右子树去找,//但实际自己被换到了左子树,所以应该一开始就在根结点的左子树找自己return _EraseR(root->_left, key);}//走到此代表待删结点的孩子已经处理好了,可以释放空间了delete del;return true;}
}

🎏实现BSTree类析构函数

        由于析构函数是按后序遍历递归删除整颗树的,所以我们给析构函数也创建一个子函数, 按先左,再右,再根的顺序递归删除二叉树即可,实现代码及注释如下:

//析构函数
~BSTree()
{Destroy(_root);
}//析构函数子递归
void Destroy(Node*& root)
{if (root == nullptr)return;Destroy(root->_left);    //先递归删左子树Destroy(root->_right);    //再递归删右子树delete root;        //最后删掉根节点root == nullptr;    //用引用传参还可以直接在这里将root置为nullptr
}

🎏实现BSTree类拷贝构造函数

        由于拷贝构造函数是按先序遍历递归拷贝整颗树的,所以我们给拷贝构造函数也创建一个子函数, 按先根,再左,再右的顺序递归拷贝整颗二叉树即可,实现代码及注释如下:

//拷贝构造
BSTree(const BSTree<K>& t)
{_root = Copy(t._root);
}//拷贝构造子函数
Node* Copy(Node* root)
{if (root == nullptr)    //如果待拷贝结点为空就直接返回给上层空结点即可return nullptr;Node* copynode = new Node(root->_key);    //先拷贝根节点copynode->_left = Copy(root->_left);      //再递归链接左子树copynode->_right = Copy(root->_right);    //后递归链接右子树return copynode;        //注意最后要将当前结点返回给上一层链接
}

🎏实现BSTree类赋值运算符重载operator=函数

        赋值运算符重载函数我们照例像之前实现STL容器那样偷个懒,直接借用形参是实参的一份临时拷贝这一特点,将this指针和待赋值的参数的形参做交换,这样就可以无痛完成赋值运算了,实现代码如下:

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

三.项目完整代码

我们将程序运行的代码分别在两个工程文件中编辑,完整代码如下:

📌test.cpp文件

        该部分文件主要包含部分二叉搜索树功能测试代码,大家可以酌情参考。

#include<iostream>
using namespace std;#include"BinarySearchTree.h"//测试插入删除函数
void testBSTree1()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : a){t.Insert(e);}t.InOrder();t.Erase(4);t.InOrder();t.Erase(6);t.InOrder();t.Erase(7);t.InOrder();t.Erase(8);t.InOrder();for (auto e : a){t.Erase(e);}t.InOrder();
}//测试递归插入删除函数
void testBSTree2()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : a){t.InsertR(e);}t.InOrder();t.EraseR(4);t.InOrder();t.EraseR(6);t.InOrder();t.EraseR(7);t.InOrder();t.EraseR(8);t.InOrder();for (auto e : a){t.EraseR(e);}t.InOrder();
}void testBSTree3()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : a){t.InsertR(e);}t.InOrder();//测试拷贝构造BSTree<int> t1(t);t1.InOrder();
}void testBSTree4()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : a){t.InsertR(e);}t.InOrder();//测试赋值运算符重载BSTree<int> t1 = t;t1.InOrder();
}
int main()
{testBSTree1();return 0;
}

📌BinarySearchTree.h文件

#pragma oncetemplate<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//缺省值的作用是在无参调用时直接去调用模板实例化的类的无参构造函数//这里一定不能将缺省值给0/nullptr!因为你不知道模板实例化的类具体到底是内置类型还是自定义类型(如Date)//所以要显示调用T类型它自己的无参构造函数BSTreeNode(const K& key = K()):_left(nullptr),_right(nullptr),_key(key){}
};template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree():_root(nullptr){}//拷贝构造BSTree(const BSTree<K>& t){_root = Copy(t._root);}//赋值BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}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;}//循环版Findbool 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.叶子  2. 一个孩子     托孤//3. 两个孩子      替换法:左子树的最大(右)结点,或者右子树的最小(左)节点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->_left == nullptr)//左空托右孤,注意左右都空也走这里{if (cur == _root){_root = cur->_right;}else{if (parent->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}else if (cur->_right == nullptr)//右空托左孤{if (cur == _root){_root = cur->_left;}else{if (parent->_right == cur){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}}else//左右都不为空{//找左树的最右值Node* parent = cur;//写nullptr必崩Node* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(cur->_key, leftMax->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur = leftMax;}delete cur;return true;}}return false;}void InOrder(){_InOrder(_root);}bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}private://将部分递归子函数放在这里,对外更好的封装这个类模板Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* copynode = new Node(root->_key);copynode->_left = Copy(root->_left);copynode->_right = Copy(root->_right);return copynode;}//析构函数子递归void Destroy(Node*& root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root == nullptr;}bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}if (root->_key < key){_EraseR(root->_right, key);}else if (root->_key > key){_EraseR(root->_left, key);}else//画图吧,不多说了{//找到了,开始按三种情况删Node*  del = root;//左空托右孤,右空托左孤,都有找接班if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{//找替代结点Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;return true;}}bool _InsertR(Node*& root,const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){_InsertR(root->_right, key);}else if (root->_key > key){_InsertR(root->_left, key);}else{return false;}}//递归Find子递归bool _FindR(Node* root, const K& key){if (root == nullptr){return false;}if (root->_key < key){_FindR(root->_right, key);}else if (root->_key > key){_FindR(root->_left, key);}else{return true;}}//中序遍历子递归void _InOrder(Node* root){if (root == nullptr){//cout << "NULL" << endl;return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}Node* _root;
};

结语

希望这篇 二叉搜索(排序)树 的模拟实现详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

相关文章推荐

【数据结构】什么是二叉搜索(排序)树?

【数据结构】C语言实现链式二叉树(附完整运行代码)

【C++】模拟实现priority_queue(优先级队列)

【C++】模拟实现queue

【C++】模拟实现stack

【C++】模拟实现list

【C++】模拟实现vector

【C++】模拟实现string类

【C++】构建第一个C++类:Date类

【C++】类的六大默认成员函数及其特性(万字详解)

【C++】什么是类与对象?


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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

胤娲科技:马斯克放大招,盲人也能“开眼看世界”你准备好了吗?

导读前沿&#xff1a; 嘿&#xff0c;朋友们&#xff0c;想象一下&#xff0c;你突然发现自己变成了一部老式黑白电视机的观众&#xff0c;屏幕模糊&#xff0c;色彩全无&#xff0c;是不是感觉人生瞬间失去了“高清”模式&#xff1f; 但别急&#xff0c;科技界的“魔术师”马…

CDVAE项目环境配置

CDVAE环境配置 1. 系统环境2. 设置环境变量3. 配置环境变量4. 安装CDVAE虚拟环境5. 资料下载 1. 系统环境 系统环境&#xff1a;Ubuntu22.04GeForce RTX 3090cuda12.6&#xff08;cuda版本11.1以上均适用&#xff09;。 2. 设置环境变量 先按照CDVAE中描述的设置环境变量。 …

Ubuntu 20.04 内核升级后网络丢失问题的解决过程

在 Ubuntu 系统中&#xff0c;内核升级是一个常见的操作&#xff0c;旨在提升系统性能、安全性和兼容性。然而&#xff0c;有时这一操作可能会带来一些意外的副作用&#xff0c;比如导致网络功能的丧失。 本人本来是想更新 Nvidia 显卡的驱动&#xff0c;使用 ubuntu-drivers …

element-ui 日期选择器禁用某段特定日期

element-ui 日期选择器设置禁用日期 效果图如下: 2024-09-01 到2024-09-18之间的日期都不可选 2024-01-01之前的日期都不可选 官方文档中 picker-options 相关的介绍 实现功能: ​ 某仓库有限制最大可放置资产数量,且资产出借和存放都有记录。由于线下仓库资产出借和购…

c++实现类

Date类的实现-->(里面涉及类&#xff0c;this指针&#xff0c;引用&#xff0c;复用&#xff0c;运算符重载&#xff0c;友元函数&#xff0c;) Date类的实现 本章节我们将根据前面所学过的知识&#xff0c;综合运用来完成一个日期类代码的实现&#xff0c;里面的知识点也能…

yolo自动化项目实例解析(二)ui页面整理 1.78

我们在上一章整理main.py 的if __name__ __main__: 内容还留下面这一段&#xff0c; from PyQt5.QtWidgets import *from lanrenauto.moni.moni import *from PyQt5.QtGui import *app QApplication(sys.argv) # 初始化Qt应用ratio screen_width / 2560 # 分辨率比例# 设…

简单题69.x的平方根 (Java)20240919

问题描述&#xff1a; java代码&#xff1a; class Solution {public int mySqrt(int x) {if (x < 2) {return x; // 0 和 1 的平方根分别是它们自己}int left 2; // 从2开始&#xff0c;因为0和1已经处理了int right x / 2; // 最大可能的平方根不会超过 x / 2int mid;w…

【6DRepNet360全范围头部姿态估计onnxruntime推理】

6DRepNet360全范围头部姿态估计 标题摘要关键词主要贡献方法概述实验结论模型转换和onnxruntime推理模型和代码下载可视化结果代码 这篇论文的核心内容是关于一种用于全范围旋转头部姿态估计的新方法。以下是关键点的总结&#xff1a; 标题 Towards Robust and Unconstrained…

1.Spring-容器-注册

一、Bean和获取Bean &#xff08;1&#xff09;创建IoC容器&#xff1a; SpringApplication.run(类名.class, args); ConfigurableApplicationContext ioc SpringApplication.run(Spring01IocApplication.class, args); &#xff08;2&#xff09;将对象注册到IoC容器中&am…

粘接黑科技标杆专业展会-ASE CHINA 2024 震撼开幕!

2024年9月19日&#xff0c;第27届国际胶粘剂及密封剂展暨第19届国际胶粘带与薄膜展&#xff08;以下简称ASE CHINA 2024&#xff09;在上海新国际博览中心N3-N4-N5馆璀璨揭幕。ASE CHINA作为粘接新材料产业风向标&#xff0c;历经27年的辛苦耕耘&#xff0c;与业界同仁并肩而行…

sql执行流程经典案例分析

现在有联合索引(a,b),select* form tb where b xx group by a执行流程是什么样子的? CREATE TABLE IF NOT EXISTS test(id INT(10) NOT NULL AUTO_INCREMENT COMMENT主键,a INT(10) NULL,b INT(10) NULL,PRIMARY KEY(id),INDEX idx_a_b(a,b))ENGINE INNODB;INSERT INTO test…

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署Grav内容管理系统

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下部署Grav内容管理系统 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、Grav介绍2.1 CMS介绍2.2 Grav简介2.3 Grav特点2.4 …

Abaqus 2024百度云下载:附中文安装包+教程

正如大家所熟知的&#xff0c;Abaqus是一款有限元分析软件&#xff0c;能够高效的配合工程师完成创作。它可以高精度地实现包括金属、橡胶、高分子材料、复合材料、钢筋混凝土、可压缩超弹性泡沫材料以及土壤和岩石等地质材料的工程仿真计算。 “Abaqus”不仅具有出色的仿真计…

ODrive电机驱动算法VScode环境配置笔记教程

1、ODrive基本介绍 ODrive 是一个开源的优秀电机控制器项目&#xff0c;旨在为各种应用提供高性能、高可靠性的电机控制解决方案。这个项目是专门用于驱动无刷直流电机&#xff08;BLDC&#xff09;和永磁同步电机&#xff08;PMSM&#xff09;的高性能开源伺服控制系统。ODriv…

15_Python中错误和异常处理

在Python编程中&#xff0c;错误&#xff08;Error&#xff09;和异常&#xff08;Exception&#xff09;是两个相关的概念&#xff0c;但它们之间有细微的区别。 错误&#xff08;Error&#xff09; 错误通常是指在执行代码时遇到的问题&#xff0c;这些问题可能会导致程序崩…

python使用vscode 所需插件

1、导读 环境&#xff1a;Windows 11、python 3.12.3、Django 4.2.11、 APScheduler 3.10.4 背景&#xff1a;换系统需要重新安装&#xff0c;避免后期忘记&#xff0c;此处记录一下啊 事件&#xff1a;20240921 说明&#xff1a;记录&#xff0c;方便后期自己查找 2、插件…

vmware官网下载

1 https://www.vmware.com/ 2 3 4 https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion

想要让ai做ppt?试试这四个!

今天咱们来聊点新鲜的&#xff0c;就是那些能让我们从繁琐的PPT制作中解脱出来的智能工具。你是否还在为制作PPT熬夜到天亮&#xff1f;别担心&#xff0c;我这就带你看看目前市面上最火的几款智能PPT生成工具&#xff0c;它们的表现如何呢&#xff1f;让我们一探究竟&#xff…

2021的OWASP TOP 10

OWASP&#xff08;开放Web应用安全项目&#xff09;是一个非营利性组织&#xff0c;旨在提高软件安全性。 每四年一个更新&#xff0c;2025年就会再次更新&#xff0c;到时候这篇文章也会实时更新。 我主要从定义&#xff0c;场景&#xff0c;原因&#xff0c;影响&#xff0…

简单水印通过python去除

简单水印通过python去除 先看效果&#xff0c;如果效果不是你需要的就可以不用浪费时间。 注意&#xff1a;这种主要还是对应的文字在水印上方的情况&#xff0c;同时最好不要有渐变水印否则可能最后输出的图片的水印还会有所残留&#xff0c;不过还是学习使用&#xff0c;相信…