[数据结构进阶 C++] 二叉搜索树(BinarySearchTree)的模拟实现

在这里插入图片描述

文章目录

  • 1、二叉搜索树
    • 1.1 二叉搜索数的概念
    • 1.2 二叉搜索树的操作
      • 1.2.1 二叉搜索树的查找
      • 1.2.2 二叉搜索树的插入
      • 1.2.3 二叉搜索树的删除
  • 2、二叉搜索树的应用
    • 2.1 K模型
    • 2.2 KV模型
  • 3、二叉搜索树的性能分析
  • 4、K模型与KV模型完整代码
    • 4.1 二叉搜索树的模拟实现(K模型)
    • 4.2 KV模型的模拟实现

1、二叉搜索树

1.1 二叉搜索数的概念

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

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树
    我们先给出两个示例:
    在这里插入图片描述
    此二叉树就不是搜索二叉树,根据性质来看,每颗子树也是二叉搜索树,红圈圈起来的地方显然是违背了这个性质。
    在这里插入图片描述
    此搜索二叉树按性质来看是完全满足的,因此此二叉树就是二叉搜索树。

1.2 二叉搜索树的操作

二叉搜索树的操作包含,增删查,下面我们就来分别模拟实现这些接口。

1.2.1 二叉搜索树的查找

思路:
a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到到空,还没找到,这个值不存在。
查找比较好理解,这里就不画图了,直接开始写代码。
1.查找的非递归方法:

bool Find(const K& key)
{Node* root = _root;while (root){if (key > root->_key)root = root->_right;else if (key < root->_key)root = root->_left;elsereturn true;}return false;
}

2.查找的递归方法:

bool FindR(const K& key)
{return _FindR(_root, key);
}bool _FindR(Node* root, const K& key)
{if (nullptr == root)return false;if (key > root->_key)return _FindR(root->_right, key);else if (key < root->_key)return _FindR(root->_left, key);elsereturn true;
}

在递归查找中,我们用户在使用的时候只需要填入要查找的数字,但是我们的查找接口需要两个参数,开始节点与查找数值,因此我们在 FindR 下再封装一层 _FindR 就可以实现了。

1.2.2 二叉搜索树的插入

二叉搜索树的插入思路:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
我们先来以图例演示一遍过程:
对下面这棵树插入一个值为11的节点
在这里插入图片描述

这里是成功插入的情况,具体过程是上面的图例,我们在梳理一遍:
1、首先,我们用两个结点指针 parent和cur ,cur不断寻找正确插入位置,parent不断记下来cur的父节点指针;
2、当 cur 找到正确位置之后,先 new 一个节点对象给 cur,此时 new 出来的对象只是给了 cur 这个指针变量,并没有链接起来;
3、判断要插入的位置是 parent 的左孩子还是右孩子,再将其链接到正确位置插入就算完成了。
失败情况:
因为是二叉搜索树,节点左子树的值全小于节点的值,右子树的值全大于节点的值,不存在相等的情况,因此当找到一个节点的值与要插入的值相等时,就是插入失败,此时返回false。
根据上面的图示以及过程的梳理我们来写非递归版本的代码:

bool Insert(const K& key)
{if (nullptr == _root){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parnet = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (key > parent->_key){parent->_right = cur;}else{parent->_left = cur;}return true;
}

insert接口我们只提供非递归版本的。

1.2.3 二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情
况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点
中,再来处理该结点的删除问题–替换法删除
1、删除节点没有左孩子

在这里插入图片描述

// 1、左为空
if (cur->_left == nullptr)
{if (cur == _root){_root = cur->_right;}if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}
}

2、删除节点没有右孩子
在这里插入图片描述

// 2、右为空
else if (cur->_right == nullptr)
{if (cur == _root){_root = cur->_left;}		if (parent->_left == cur){parent->_left = cur->_left}else{parent->_right = cur->_left;}
}

3、删除节点左右孩子都存在
在这里插入图片描述

// 3、左右都不为空
else
{Node* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;elseparent->_right = subLeft->_right;delete(subLeft);
}

整个删除接口合在一起的代码:

bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parnet = cur;cur = cur->_right}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{// 1、左为空if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}delete(cur);}// 2、右为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}		if (parent->_left == cur){parent->_left = cur->_left}else{parent->_right = cur->_left;}delete(cur);}// 3、左右都不为空else{Node* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;elseparent->_right = subLeft->_right;delete(subLeft);}}}	
}

递归版本:
递归版本与非递归版本类似, 非递归版本中使用while循环来找key位置,递归就是不断调用自身来找key位置当找到后依然分三种情况来分析:左为空、右为空、左右都不为空。
递归版本中用引用来接受传来的root,因此不用考虑链接问题也就不存在判断删除位置是父节点的左还是右,直接修改root即可。
1、左为空/左右都为空:先记下要删除的节点,再修改root,最后释放删除的节点。 Node* del = root; root = root->_right; delete(del);
2、右为空:先记下要删除的节点,再修改root,最后释放删除的节点。Node* del = root; root = root->_left; delete(del);
3、左右都不为空:先找到右子树的最左节点(最小值),交换 root->_key与subLeft->_key, 右子树的最左节点一定是叶子节点,因此删除subLeft直接再去调用函数自身即可,即再来一次左右都为空(左为空)的删除。

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{if (root->_left == nullptr){Node* del = root;root = root->_right;delete(del);// 必须释放,不释放会导致内存泄漏return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete(del);return true;}else{Node* subLeft = root->_right;while (subLeft->_left){subLeft = subLeft->_left;}swap(root->_key, subLeft->_key);return _EraseR(root->_right, key);}}
}

2、二叉搜索树的应用

2.1 K模型

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

  • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2.2 KV模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。 该种方式在现实生活中非常常见:
● 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
● 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

3、二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
在这里插入图片描述
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log_2 N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N

最差情况是退化为单支树,性能就变得很差了,因为后续我们将改进搜索二叉树的插入与删除,来实现平衡二叉搜索树,即AVL树与红黑树(RB树)。

4、K模型与KV模型完整代码

4.1 二叉搜索树的模拟实现(K模型)

namespace key
{template <class K>struct BSTreeNode{BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}};template <class K>class BSTree{typedef BSTreeNode<K> Node;public: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;}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 true;}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{// 删除分情况讨论:// 1. 左为空(左右都为空);2. 右为空;3.左右都不为空if (cur->_left == nullptr)// 左为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_right;else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete(cur);}else if (cur->_right == nullptr)// 右为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_left;else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete(cur);}else// 左右都不为空{// 替换法,找左子树最大 / 右子树最小来替换curNode* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;else// 处理删除根节点的情况parent->_right = subLeft->_right;delete(subLeft);}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}//递归bool InsertR(const K& key){return _InsertR(_root, key);}bool FindR(const K& key){return _FindR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}// C++11BSTree() = default; // 强制编译器生成默认构造BSTree(const BSTree<K>& t){_root = Copy(t._root);}// 赋值重载,现代写法BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}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;root = nullptr;}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;}}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;}}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{if (root->_left == nullptr){Node* del = root;root = root->_right;delete(del);// 必须释放,不释放会导致内存泄漏return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete(del);return true;}else{Node* subLeft = root->_right;while (subLeft->_left){subLeft = subLeft->_left;}swap(root->_key, subLeft->_key);return _EraseR(root->_right, key);}}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root = nullptr;};
};

4.2 KV模型的模拟实现

namespace kv
{template <class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _val;BSTreeNode(const K& key, const V& val):_left(nullptr), _right(nullptr), _key(key), _val(val){}};template <class K, class V>class BSTree{typedef BSTreeNode<K, V> Node;public:bool Insert(const K& key, const V& val){if (_root == nullptr){_root = new Node(key, val);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, val);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* 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 cur;}}return nullptr;}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{// 删除分情况讨论:// 1. 左为空(左右都为空);2. 右为空;3.左右都不为空if (cur->_left == nullptr)// 左为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_right;else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete(cur);}else if (cur->_right == nullptr)// 右为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_left;else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete(cur);}else// 左右都不为空{// 替换法,找左子树最大 / 右子树最小来替换curNode* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;else// 处理删除根节点的情况parent->_right = subLeft->_right;delete(subLeft);}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " : " << root->_val << endl;_InOrder(root->_right);}private:Node* _root = nullptr;};
};

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

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

相关文章

SuperMap Hi-Fi 3D SDK for Unity矢量面贴地贴模型

作者&#xff1a;kele 一、背景 SuperMap Hi-Fi 3D SDK&#xff08;2023 11i&#xff09; for Unity推出新功能&#xff1a;支持矢量面同时贴地形图层和模型图层&#xff0c;并且能实现数据点击查询属性、更改初始填充颜色、初始边框线颜色、选中填充颜色、选中边框线颜色、控…

Redis-学习笔记

Remote Dictionary Server(Redis) 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库&#xff0c;并提供多种语言的 API&#xff0c;是跨平台的非关系型数据库。 Redis 通常被称为数据结构服务器&…

系列十二(面试)、Java中的GC回收类型有哪些?

一、Java中的GC回收类型 1.1、概述 Java中的GC回收类型主要包含以下几种&#xff0c;即&#xff1a;UseSerialGC、UseParallelGC、UseConcMarkSweepGC、UseParNewGC、UseParallelOldGC、UseG1GC。 1.2、源码

【Linux笔记】文件查看和编辑

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux学习 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 命令 cat (Concatenate and Display): more 和 less: nano 和 vim (文本编辑器): 结语 我的其他博客 前言 学习Linux命令行和文件…

EfficientDet:Scalable and Efficient Object Detection中文版 (BiFPN)

EfficientDet: Scalable and Efficient Object Detection EfficientDet&#xff1a;可扩展和高效的目标检测 摘要 模型效率在计算机视觉中变得越来越重要。本文系统地研究了用于目标检测的神经网络架构设计选择&#xff0c;并提出了几个关键的优化方法来提高效率。首先&…

Leetcode—剑指Offer LCR 025.两数相加II【中等】

2023每日刷题&#xff08;六十七&#xff09; Leetcode—LCR 025.两数相加II 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode…

State of PostgreSQL 2023 报告解读

基于 PostgreSQL 内核的时序数据库厂商 Timescale 发布了一年一度的 State of Postgres 2023 报告。 Timescale 介绍 简单先介绍一下 Timescale 这家公司的历史。它最早是提供了一个 PG 的插件&#xff0c;引入了 Hypertable 这个概念&#xff0c;来高效地处理时序数据&…

飞天使-k8s知识点5-kubernetes基础名词扫盲

文章目录 deploymentspodNodeserviceskubectl 实现应用伸缩kubectl 实现滚动更新kubernetes架构 deployments 中文文档 http://docs.kubernetes.org.cn/251.htmldeployment是用来创建和更新应用的&#xff0c;master 会负责将创建好的应用实例调度到集群中的各个节点 应用实例…

YOLOv8算法改进【NO.96】针对小目标检测有效果的ASF-YOLO

前 言 YOLO算法改进系列出到这&#xff0c;很多朋友问改进如何选择是最佳的&#xff0c;下面我就根据个人多年的写作发文章以及指导发文章的经验来看&#xff0c;按照优先顺序进行排序讲解YOLO算法改进方法的顺序选择。具体有需求的同学可以私信我沟通&#xff1a; 第一…

qt连接hiki相机进行拍照保存

.pro QT工程pro文件模板变量&#xff08;TEMPLATE&#xff09;模板变量告诉qmake为这个应用程序生成哪种makefile。下面是可供使用的选择&#xff1a; app - 建立一个应用程序的makefile。这是默认值&#xff0c;所以如果模板没有被指定&#xff0c;这个将被使用。 lib - …

计算机体系结构实验——Branch-Target Buffers

实验五 Branch-Target Buffers 本次实验的主要目的是加深对Branch-Target Buffers的理解。掌握使用Branch-Target Buffers减少或增加分支带来的延迟的情况。 实验内容&#xff1a; 将以下程序段修改为可利用WinMIPS64模拟器运行的程序。假设R3的初始值为R240 在使用forward…

业务逻辑漏洞有哪些?漏洞攻击防御及代码示例

文章目录 简介危害成因攻击防御代码示例1. 未经验证的重要操作2. 认证绕过3. 逻辑时间窗口漏洞4. 负载测试漏洞 修复 业务逻辑漏洞是指软件或系统的逻辑设计上的缺陷&#xff0c;这些缺陷可能被攻击者利用&#xff0c;从而导致意料之外的行为。下面是对业务逻辑漏洞的简介、危害…

基于 FFmpeg 的跨平台视频播放器简明教程(十二):Android SurfaceView 显示图片和播放视频

系列文章目录 基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;一&#xff09;&#xff1a;FFMPEG Conan 环境集成基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;二&#xff09;&#xff1a;基础知识和解封装&#xff08;demux&#xff09;基于 FFmpeg 的跨平台视频…

【LeetCode:2866. 美丽塔 II | 单调栈 + 前后缀数组】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

flink watermark 实例分析

WATERMARK 定义了表的事件时间属性&#xff0c;其形式为: WATERMARK FOR rowtime_column_name AS watermark_strategy_expression rowtime_column_name 把一个现有的列定义为一个为表标记事件时间的属性。该列的类型必须为 TIMESTAMP(3)/TIMESTAMP_LTZ(3)&#xff0c;且是 sche…

2023年12月GESP认证图形化编程四级真题试卷

2023年12月GESP认证Scratch图形化等级考试&#xff08;四级&#xff09;真题试卷 题目总数&#xff1a;27 总分数&#xff1a;100 选择题 第 1 题 单选题 现代计算机是指电子计算机&#xff0c;它所基于的是&#xff08; &#xff09;体系结构 A. 艾伦图灵 B. …

Valentina Studio Pro for Mac:高效数据库管理工具

作为一款强大而高效的数据库管理工具&#xff0c;Valentina Studio Pro for Mac在Mac平台上的表现无疑是令人印象深刻的。无论您是初学者还是专业数据库管理员&#xff0c;Valentina Studio Pro都能够满足您的需要&#xff0c;并提供一流的工具和功能来简化数据库管理的过程。 …

KBU808-ASEMI适配高端电源KBU808

编辑&#xff1a;ll KBU808-ASEMI适配高端电源KBU808 型号&#xff1a;KBU808 品牌&#xff1a;ASEMI 封装&#xff1a;KBU-4 最大平均正向电流&#xff1a;8A 最大重复峰值反向电压&#xff1a;800V 产品引线数量&#xff1a;4 产品内部芯片个数&#xff1a;4 产品内…

Docker 编译OpenHarmony 4.0 release

一、背景介绍 1.1、环境配置 编译环境&#xff1a;Ubuntu 20.04OpenHarmony版本&#xff1a;4.0 release平台设备&#xff1a;RK3568 OpenHarmony 3.2更新至OpenHarmony 4.0后&#xff0c;公司服务器无法编译通过&#xff0c;总是在最后几十个文件时报错,错误码4000&#xf…

C#电源串口调试

目的 记录串口调试的遇到的一些问题以及相应的解决方法 1.串口定义:串口是计算机与其他硬件传输数据的通道&#xff0c;在计算机与外设通信时起到重要作用 2.串口通信的基础知识 C#中的串口通信类 C#使用串口通信类是SerialPort(),该类使用方法是 new 一个 SerialPort对象 为S…