【C++进阶】二叉树进阶之二叉搜索树

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、什么是二叉搜索树
  • 二、二叉搜索树的实现(迭代版)
      • 2.1 定义二叉树的结点
      • 2.2 插入操作
      • 2.3 查找操作
      • 3.4 删除操作
      • 3.5 二叉搜索树的遍历
        • 3.5.1 中序遍历(重点)
        • 3.5.2 前序遍历
        • 3.5.3 后序遍历
  • 四、二叉搜索树的实现 (递归版)
      • 4.1 查找操作
      • 4.2 插入操作
      • 4.3 删除操作
  • 五、代码补充
      • 5.1 释放结点
      • 5.2 拷贝构造
      • 5.3 赋值运算符重载
  • 六、性能分析
  • 七、完整代码
        • 7.1 非递归
      • 7.2 递归

一、什么是二叉搜索树

二叉搜索树(Binary Search Tree)是基于二叉树的一种改进版本。因为普通二叉树没有实际价值,无法进行插入、删除等操作,但二叉搜索树就不一样了。

二叉搜索树又称二叉排序树,它或者是一棵空树。如果不为空则满足一下性质:

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

总之,左节点比根小,右节点比根大。因此二叉搜索树的查找效率极高,具有一定的实际价值。

下图展示了二叉搜索树

在这里插入图片描述

搜索二叉树还有一个非常重要的特性:中序遍历(左子树 根 右子树)的结果为升序。

二、二叉搜索树的实现(迭代版)

2.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.2 插入操作

  1. 当插入一个值时,必须先找到满足二叉搜索性质的位置
  2. 如果当前位置不为空或者插入的值和已有的值重复,则插入失败。
  3. 如果找到满足条件的位置并且为空,则结束循环,进行插入。步骤:创建新节点、判断需要插在左边还是右边、链接新节点
template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree():_root(nullptr){}// 插入操作bool Insert(const K& key){// 一开始树为空,直接插入if (_root == nullptr){_root = new Node(key);return true;}// cur用来查找合适的位置Node* cur = _root;// parent用来找cur的父亲结点Node* parent = nullptr;while (cur){// 插入的结点大于当前结点// 往右走if (cur->_key < key){parent = cur;cur = cur->_right;}// 插入的结点小于当前结点// 往左走else if (cur->_key > key){parent = cur;cur = cur->_left;}// 最后一种情况就是出现冗余// 直接返回else{return false;}}// 当循环来到此处说明已经找到合适的位置了// 直接开始插入操作// 1. 创建新的结点cur = new Node(key);// 2. 判断是插在父亲的左边还是右边if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}
private:Node* _root;
};

【插入成功】

在这里插入图片描述

【插入失败】

在这里插入图片描述

2.3 查找操作

  1. 从根结点开始比较,比根大的则往右查找;比根小的往左查找。
  2. 走到空还没找到,说明值不存在。否则就是存在
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;}

【查找成功】

在这里插入图片描述

【查找失败】

在这里插入图片描述

3.4 删除操作

二叉搜索树的删除是个麻烦事,需要考虑很多情况,因此大多数面试都会考察搜索二叉树的删除操作

删除逻辑:

首先查找元素是否在二叉搜索树中,如果不存在,则直接返回;否在要删除的结点就要分以下四种情况

1、 要删除的结点只有左孩子

只需要将被删除结点的左子树与父节点进行链接即可。前提是要判断删除的结点是父结点的左还是右。

在这里插入图片描述

2、 要删除的结点只有右孩子

同理,左子树为空时,将其右子树与父节点进行判断链接,链接完成后删除目标节点

3、要删除的结点有两个孩子

当左右都不为空时,就有点麻烦了,需要找到一个合适的值(即>左子树所有节点的值,又<右子树所有节点的值),确保符合二叉搜索树的基本特点

符合条件的值有:左子树的最右节点(左子树中最大的)或者右子树的最左节点(右子树中最小的),将这两个值中的任意一个覆盖待删除节点的值,都能确保符合要求

以左子树的最右节点(左子树中最大的)为例:

在这里插入图片描述

4、要删除的结点没有孩子

这种情况可以包含在1或者2中

下面是代码实现(详细的代码解释)

bool Erase(const K& key)
{// parent - 用来记录删除结点的父亲Node* parent = nullptr;// cur - 记录被删除的结点Node* cur = _root;// 查找key(被删除的结点)while (cur){// 如果key比当前元素要大// 就往右找if (cur->_key < key){parent = cur;cur = cur->_right;}// key比当前元素要小// 就往左找else if (cur->_key > key){parent = cur;cur = cur->_left;}// 最后一种情况就是找到了// 对于找到要分情况讨论	else {// 1. 如果删除的结点的左孩子为空// 右孩子可能为空(包含删除叶子结点的情况)// 也可能不为空if (cur->_left == nullptr){// 可能删除的结点是根结点// 也就是以下这种情况// 1 --> root cur//   2//     3// 没有考虑这个问题会引发parent野指针问题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;}}} // 左右都不为空 // 左右子树都不为空的场景中// parent 要初始化为cur,避免后面的野指针问题else{// 找替代节点// 以左子树的最右节点(左子树中最大的)为例Node* parent = cur;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;
}

3.5 二叉搜索树的遍历

二叉搜索树的遍历操作和二叉树一模一样的,因此可以有前序遍历、中序遍历以及后序遍历。

3.5.1 中序遍历(重点)

搜索二叉树的 中序遍历(左子树 根 右子树)的结果为升序

void InOrder(Node* root)
{if (root == NULL){return;}InOrder(root->_left);cout << root->_key << " ";InOrder(root->_right);
}

但因为这里是一个被封装的类,所以面临着一个尴尬的问题:二叉搜索树的根root是私有,外部无法直接获取

解决办法:

  1. 公有化(破坏类的封装性,不推荐)
  2. 使用静态成员函数可以直接被类外使用。(也是ok,但是不推荐)
  3. 将这种需要用到根的函数再封装。(推荐)
public:
void InOrder()
{_InOrder(root);
}
private:
void _InOrder(Node* root)
{if (root == NULL){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);
}

实际调用时,只能调到InOrder,因为真正的函数_InOrder为是私有,除了类之外,其他地方不可访问。

3.5.2 前序遍历

public:
void PreOrder()
{_PreOrder(_root);
}private:void _PreOrder(Node* root){if (root == nullptr){return;}cout << root->_key << " ";_PreOrder(root->_left);_PreOrder(root->_right);}

3.5.3 后序遍历

public:
void PostOrder()
{_PostOrder(_root);
}
private:void _PostOrder(Node* root)
{if (root == nullptr){return;}_PostOrder(root->_left);_PostOrder(root->_right);cout << root->_key << ' ';
}

四、二叉搜索树的实现 (递归版)

4.1 查找操作

递归查找逻辑:

  1. 如果当前根的值<查找值,递归至右树查找
  2. 如果当前根的值>查找值,递归至左树查找

二叉树的递归常常是通过将问题分解为更小规模的子问题来解决的。通过传递根节点作为参数,我们可以在每次递归中处理当前节点,并将问题分解为处理左子树和右子树的子问题。因此,还需要对函数进行封装。

public:
bool Find(const K& key)
{return _Find(_root, key);
}
private:
bool _Find(Node* root, const k& key)
{// 如果根为空,说明查找失败(递归出口)if (root == nullptr)return false;// 查找的值比当前结点大,往右找if (root->_key < key){return _Find(root->_right);}// 查找的值比当前结点小,往左找else if (root->_key > key){return _Find(root->_left);}// 最后一种情况就是找到了elsereturn true;
}

4.2 插入操作

这里和其它不同的是,在传参的时候使用了引用传引用是为了在递归查找过程中能够修改目标节点的指针值。搜索二叉树的递归查找算法通常会通过比较当前节点的值与目标值来决定向左子树还是右子树继续查找。如果不传引用,每次递归调用时都会创建一个新的节点指针副本,这样就无法修改原始节点的指针值,从而导致无法正确返回目标节点的指针。

这么说有点抽象,可以先看代码理解;过会画完递归展开图大家就能够理解了。

public:
bool Insert(const K& key)
{return _Insert(_root, key);
}private:
bool _Insert(Node*& root, const K& key)
{// 当走到空,说明找到了合适的位置if (root == nullptr){// 还得找到父亲,为什么加个引用就搞定了?root = new Node(key);return true;}// 插入的值比当前结点大,往右找if (root->_key < key){return _Insert(root->_right);}// 插入的值比当前结点小,往左找else if (root->_key > key){return _Insert(root->_left);}// 当插入的值和树发生冗余,直接返回falseelse{return false;}
}

【递归展开图】

在这里插入图片描述

4.3 删除操作

递归删除时也使用了引用,其想法和插入一样,不需要找删除结点的父亲,直接修改目标节点的指针值

public:
bool Earse(const K& key)
{return _Erase(root, key);
}private:
bool _Erase(Node* root, const K& key)
{// 如果是空结点,说明删除失败if (root == nullptr)return false;if (root->_key < key){return _Erase(root->_right, key);}else if (root->_key > key){return _Erase(root->_left, key);}// 找到keyelse{Node* del = root;// 还是分三种情况// 1. 删除结点的左孩子为空if (root->_left == nullptr){root = root->_right;}// 2. 删除结点的右孩子为空else if (root->_right == nullptr){root = root->_left;}// 3. 删除结点孩子都不为空else{//递归为小问题去处理Node* maxLeft = root->_left;while (maxLeft->_right){maxLeft = maxLeft->_right;}//注意:需要交换std::swap(root->_key, maxLeft->_key);//注意:当前找的是左子树的最右节点,所以递归从左子树开始return _Erase(root->_left, key);}delete del;	//释放节点return true;}
}	

五、代码补充

5.1 释放结点

创建节点时,使用了new申请堆空间,根据动态内存管理原则,就需要使用delete释放申请的堆空间,但二叉搜索树是一棵树,不能直接释放,需要递归式的遍历每一个节点

释放思路:后序遍历思想

public:
~BSTree()
{_destory(_root);
}private:
void _destory(Node*& root)
{if (root == nullptr)return;//后序遍历销毁destory(root->_left);destory(root->_right);delete root;root = nullptr;
}

5.2 拷贝构造

销毁问题考虑完以后,就要想是否会有浅拷贝问题,因为浅拷贝问题会导致一个结点析构两次,程序就崩了。而我们在类和对象中说过,如果类中有动态分配内存的指针变量,则需要手动编写深拷贝的拷贝构造函数。

public:
BSTree(BSTree<K>& tree):_root(nullptr)
{_root = _Copy(tree._root);
}private:
Node* _Copy(Node* root)
{//递归拷贝if (root == nullptr)return nullptr;Node* new_root = new Node(root->_key);	new_root->_left = _Copy(root->_left);new_root->_right = _Copy(root->_right);return new_root;
}

5.3 赋值运算符重载

如果没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝(浅拷贝)。其默认成员函数和拷贝构造的默认函数类似:内置类型成员变量是直接赋值的,而自定义类型成员会去调用它的默认函数。因此赋值运算符也需要自己实现深拷贝

直接写现代写法

BSTree<K> operator=(BSTree<K> tree)
{std::swap(_root, tree._root);return *this;
}

六、性能分析

从名字上来看,二叉树的特性就是查找快。

搜索二叉树的时间复杂度取决于树的高度。因此当是一颗平衡二叉搜索树时,时间复杂度是O(log n)。因为每次搜索都可以通过比较目标值与当前节点的值来确定向左子树还是右子树进行搜索,这样每次都可以将搜索范围减半。
是不是有点类似于二分查找,但二分查找不是一个很实用的算法。因为对比二叉搜索树来说,二分查找(底层是一个数组)的删除和插入的效率低0(n)(特别是中间插入和删除)

在这里插入图片描述

二叉搜索树看起来这么完美,但下限没有保障。在最坏的情况下,当搜索二叉树是一个不平衡的树时,时间复杂度为O(n),其中n是树中节点的数量。这是因为在最坏情况下,每次搜索都要遍历树的所有节点。

在这里插入图片描述

因此,为了解决这个问题,引入了AVL树和红黑树(后续会讲解)

七、完整代码

7.1 非递归

#pragma once// 迭代版本
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:BSTree():_root(nullptr){}bool Insert(const K& key){// 如果一开始树为空,直接插入if (_root == nullptr){// 对自定义类型的new,会自动调用其默认构造函数_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;}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 = _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;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);}void PreOrder(){_PreOrder(_root);}void PostOrder(){_PostOrder(_root);}private:void _PostOrder(Node* root){if (root == nullptr){return;}_PostOrder(root->_left);_PostOrder(root->_right);cout << root->_key << ' ';}void _PreOrder(Node* root){if (root == nullptr){return;}cout << root->_key << " ";_PreOrder(root->_left);_PreOrder(root->_right);}void _InOrder(Node* root){if (root == NULL){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root;
};

7.2 递归

#pragma once
template<class K>
struct BSTNode
{BSTNode<K> _left;BSTNode<K> _right;K _key;BSTNode(const K& key):_left(nullptr), _right(nullptr), key(key){}
};template<class K>
class BSTree
{typedef BSTNode<K> Node;
public:BSTree():_root(nullptr){}~BSTree(){_destory(_root);}bool Find(const K& key){return _Find(_root, key);}bool Insert(const K& key){return _Insert(_root, key);}bool Earse(const K& key){return _Erase(root, key);}BSTree(BSTree<K>& tree):_root(nullptr){_root = _Copy(tree._root);}BSTree<K> operator=(BSTree<K> tree){std::swap(_root, tree._root);return *this;}
private:Node* _Copy(Node* root){//递归拷贝if (root == nullptr)return nullptr;Node* new_root = new Node(root->_key);	new_root->_left = _Copy(root->_left);new_root->_right = _Copy(root->_right);return new_root;}void _destory(Node*& root){if (root == nullptr)return;//后序遍历销毁destory(root->_left);destory(root->_right);delete root;root = nullptr;}bool _Find(Node* root, const k& key){if (root == nullptr)return false;// 查找的值比当前结点大,往右找if (root->_key < key){return _Find(root->_right);}// 查找的值比当前结点小,往左找else if (root->_key > key){return _Find(root->_left);}// 最后一种情况就是找到了elsereturn true;}bool _Insert(Node*& root, const K& key){// 当走到空,说明找到了合适的位置if (root == nullptr){// 还得找到父亲,为什么加个引用就搞定了?root = new Node(key);return true;}// 插入的值比当前结点大,往右找if (root->_key < key){return _Insert(root->_right);}// 插入的值比当前结点小,往左找else if (root->_key > key){return _Insert(root->_left);}// 当插入的值和树发生冗余,直接返回falseelse{return false;}}bool _Erase(Node* root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _Erase(root->_right, key);}else if (root->_key > key){return _Erase(root->_left, key);}else{Node* del = root;// 还是分三种情况// 1. 删除结点的左孩子为空if (root->_left == nullptr){root = root->_right;}// 2. 删除结点的右孩子为空else if (root->_right == nullptr){root = root->_left;}// 3. 删除结点孩子都不为空else{//递归为小问题去处理Node* maxLeft = root->_left;while (maxLeft->_right){maxLeft = maxLeft->_right;}//注意:需要交换std::swap(root->_key, maxLeft->_key);//注意:当前找的是左子树的最右节点,所以递归从左子树开始return _Erase(root->_left, key);}delete del;	//释放节点return true;}}private:Node* _root;
};

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

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

相关文章

Vector底层原理——面试之我答

Vector概述 vector是STL中最常用的容器&#xff0c;vector主要功能是作动态数组来弥补传统数组的缺点&#xff0c;如&#xff1a;不灵活&#xff0c;不方便插入等等。 Vector支持随机访问&#xff0c;因此访问某一个元素的时间复杂度是O(1)。 vector中存储着许多易用的函数方法…

Azure + React + ASP.NET Core 项目笔记一:项目环境搭建(二)

有意义的标题 pnpm 安装umi4 脚手架搭建打包语句变更Visual Studio调试Azure 设置变更发布 pnpm 安装 参考官网&#xff0c;或者直接使用npm安装 npm install -g pnpmumi4 脚手架搭建 我这里用的umi4&#xff0c;官网已附上 这里需要把clientapp清空&#xff0c;之后 cd Cl…

【JAVA】String类

作者主页&#xff1a;paper jie_的博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVASE语法系列》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和…

基于SSM的高校共享单车管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

Redis原理:IntSet

&#xff08;笔记总结自b站黑马程序员课程&#xff09; 一、结构 IntSet是Redis中set集合的一种实现方式&#xff0c;基于整数数组来实现&#xff0c;并且具备长度可变、有序等特征。 结构如下&#xff1a; typedef struct intset {uint32_t encoding; //编码方式uint32_t l…

读取XML的几种方式

一、为什么使用XML 1、便于不同应用程序之间通信。 2、便于不同平台之间通信。 3、便于不同平台之间数据共享。 二、Dom读取 xml文件内容 <?xml version"1.0" encoding"UTF-8"?> <bookstore><book id"1"><name>冰…

python main 函数-启动-传递参数 python 打包 exe C# 进程传参

Part1:Python main 传递参数 在Python编程中&#xff0c;我们经常需要从命令行或其他外部环境中获取参数。Python提供了一种简单而灵活的方式来处理这些参数&#xff0c;即通过main函数传参 1.python main 函数-启动-传递参数 test.py import sysdef main():# 获取命令行参…

C盘清理教程

C盘清理教程 首先使用space Sniffer 扫一下c盘&#xff0c;然后看一下到底是哪个文件这么大 第二步&#xff0c;创建软链接。 首先将我们需要移动的文件的当前路径拷贝下来&#xff1a;C:\Users\Tom\Desktop\test-link\abc\ghi.txt 然后假设剪切到D盘下&#xff1a;D:\ghi.…

基于改进人工蜂群算法的 K 均值聚类算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

RBTree模拟实现

一、概念 概念&#xff1a;红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩倍&a…

pdf拆分成多个文件的方法以及注意事项

PDF是一种非常流行的文件格式&#xff0c;但有时候我们需要将一个大的PDF文件拆分成多个小的文件&#xff0c;以便于管理和分享。本文将介绍一些拆分PDF文件的方法以及需要注意的事项。 AdobeAcrobat是一款专业的PDF编辑软件&#xff0c;可以轻松地拆分PDF文件。以下是使用Adob…

【建站教程】使用阿里云服务器怎么搭建网站?

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网以搭建WordPress网站博客为例&#xff0c;阿小云来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流程&#x…

【2023研电赛】兆易创新命题三等奖: 低成本单母线电流永磁同步无感驱动器

本文为2023年第十八届中国研究生电子设计竞赛兆易创新企业命题三等奖以及决赛最佳论文奖分享&#xff0c;参加极术社区的【有奖活动】分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来领&#xff01;&#xff0c;分享2023研电赛作品扩大影响力&#xff0c;更有…

通过阿贝云免费云服务器部署vue3+vite项目

通过阿贝云免费云服务器部署vue3vite项目 阿贝云&#xff1a;https://www.abeiyun.com 首先访问阿贝云登录后申请服务器&#xff0c;需要关注微信公众号绑定 然后我们给服务器安装操作系统&#xff0c;这里我使用了centos7.6 这里我使用finalshell 连接服务器 我们首先配置ng…

EPICS电机支持(asynMotor)

EPICS电机支持 1&#xff09; 顶层对象是EPICS motor记录 已经对这个对象编写了很多代码&#xff1a;spec&#xff0c;IDL和Python类等 2&#xff09;下一层是EPICS设备支持 了解motor记录&#xff0c;与驱动会话 3&#xff09;最底层是EPICS驱动 对motor记录一无所知&am…

如何提取视频中的音频?几个步骤轻松提取

在现今社交媒体的风靡下&#xff0c;许多人都会使用手机录制视频来记录生活中的美好瞬间。有时候&#xff0c;我们也会想要提取视频中的音频&#xff0c;例如将自己的演讲录音分发给听众。本文将会介绍如何在手机上提取视频中的音频以及需要注意的事项。 使用应用程序 首先&am…

Linux命令200例:write用于向特定用户或特定终端发送信息

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0…

Android高德地图截屏功能(可包含自定义控件)

一、不包含自定义控件 地图 SDK 支持对当前屏幕显示区域进行截屏&#xff0c;可以对地图、覆盖物&#xff08;包含信息窗口&#xff09;、Logo进行截取屏幕&#xff0c;这其中不包括地图控件、Toast窗口。 详细示例如下&#xff1a; // 对地图进行截屏aMap!!.getMapScreenSho…

正交试验设计法

正交实验设计 一、什么是正交试验设计法&#xff1f; 是一种成对测试交互的系统的统计方法。它提供了一种能对所有变量对的组合进行典型覆盖&#xff08;均匀分布&#xff09;的方法。 可以从大量的试验点中挑出适量的、有代表性的点&#xff0c;利用“正交表”&#xff0c;…

Python之数据库(MYSQL)连接

一&#xff09;数据库SQL语言基础 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS (Relational Database…