【STL】平衡二叉树

前言

对于之前普通的二叉搜索树,其搜索的效率依靠树的形状来决定,如下:

d6b39cc3ecbe4318a5bbd780ecefb9b3.png

可以看到 A图 中的树比较彭亨,搜索一个元素的效率接近 O(logN) ;而 B图 中的形状也符合搜索二叉树,但是很不平衡,这时的搜索效率就变成 O(N) 了(如搜索值为4的节点)

可以看到,如果搜索二叉树不平衡,他的搜索的效率不确定,在 O(logN) 和 O(N) 之间,可能有暴击;

那么对此,既然不平衡,那我们就给出平衡的两种方法:

1.构成AVL树

2.构成红黑树

这两种方法都是为了平衡搜索二叉树而生

AVL树

1. AVL树的概念和性质

它的出现是为了 搜索二叉树的平衡,那就来了解AVL树的概念和性质,也就是什么是AVL,并且作为一颗AVL树的要求是什么

首先AVL树是一颗树,它要求任何一个节点的左右子树的高度差不超过一,以此来保证平衡,是AVL树的核心

来看看它的做法

与搜索二叉树不同,它将二叉链,变成了三叉链(多了指向父节点的指针),并且引入了平衡因子这个东西;这些都是为了方便并且为了树的一个平衡引入

三叉链和平衡因子和下面AVL树属性的图结合起来看更直观

三叉链也就是多了一个 parent指针 指向该节点的父节点,这一操作可以让节点找到其祖先

平衡因子是指该节点的 右子树高度 减去 左子树高度的值,而根据AVL树的性质(要求任何一个节点的左右子树的高度差不超过一),也就是平衡因子 _bf 的值域为 { -1, 0, 1 } ,如果平衡因子 _bf 的值出现为 2 或者 -2 ,说明该树已经不平衡,此时需要进行停止调整,先进行旋转操作

2. AVL树类的属性

这里的节点值类型简化为了int类型,原为 pair

8d3c11a09a094a64b8137292a659eedc.png

bd509a65fd8946ffbb38ca7fb689e048.png

3. AVL树的插入函数

而AVL树的重点呢,也就是AVL树是如何让树达到平衡的呢?就是每插入了一个节点后,进行了一些调整,如果插入节点后不平衡,那么会将其调整成平衡

插入后对插入节点祖先的平衡因子进行调整,当调整过程中,有平衡因子出现异常时,此时通过旋转的方式,让树继续保持平衡

当平衡因子出现异常即是 平衡因子的值更新为 2 或者 -2 时,说明树现在已经不平衡,需要进行旋转

分情况讨论:

a6f7bbde65044588ad1a8e0c6d46229b.png

现在平衡因子已经出现了不平衡,为什么旋转之后,树就可以继续平衡呢?

具体看到旋转 (这里以左单旋为例):

定义节点指针 parent(图中的节点值为30), cur(图中的60)

左单旋是将 cur 的左孩子给给 parent 的右, 再把 parent 赋值给 cur 的左,旋转完成

再进行平衡因子的更新:parent 和 cur 的平衡因子 _bf 都变成 0

32e08c9f357a4f2db020b7bdfb4e58a1.png

左单旋具体代码如下:

        void RotateL(Node* parent){assert(parent);Node* cur = parent->_right;Node* cur_left = cur->_left;// 改变节点之间的关系parent->_right = cur_left;cur->_left = parent;// 改变_parent的指向//原本 parent 的 _parentNode* ppNode = parent->_parent;//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把             ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 curif (ppNode){if (ppNode->_left == parent){ppNode->_left = cur;}else{ppNode->_right = cur;}cur->_parent = ppNode;}//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根else{_root = cur;cur->_parent = nullptr;}if (cur_left){cur_left->_parent = parent;}parent->_parent = cur;// 更新平衡因子cur->_bf = parent->_bf = 0;}

对左单旋的总结是:当不平衡的树进行左单旋之后,树又被调整为平衡,然后再进行平衡因子的更新

那么右单旋的思路是一样的,自己可以进行推理

而要进行双旋时候,可以再进行讨论(这里以右左旋进行讨论):

8cd424f049604210a6c94074d65f57a3.png

右左旋:定义 parent(下图节点值为30), cur(下图节点值为90), cur_left(下图节点值为60)

双旋都是两大步,这里右左旋就是先进行右旋,再进行左旋,旋转结束;对,这里对刚刚实现的单旋代码进行了复用,复用yyds

具体是对 cur 先进行右旋,再对 parent 进行左旋,如下

dcb5820617d4473c95c273a54add59f1.png

最后进行平衡因子的调整,但是这里特别容易忘记第三种情况的讨论,AVL树的这个细节得注意:

188fbf8bb0974858b161474841cdfb82.png

右左旋的代码如下:

        void RotateRL(Node* parent){Node* cur = parent->_right;Node* cur_left = cur->_left;int bf = cur_left->_bf;RotateR(parent->_right);RotateL(parent);parent->_bf = 0;cur->_bf = 0;cur_left->_bf = 0;if (bf == -1){cur->_bf = 1;}if (bf == 1){parent->_bf = -1;}}

那么左右旋的思路是一样的,自己可以进行推理

4. 总结

而进行何种旋转本质是取决于 parent cur 和 cur_left (或cur_right)之间的关系,若他们三个所连成的是直线,那么进行单旋操作,如果为折线,那就进行双旋操作;而这里的平衡因子刚好可以体现他们之间的关系,所以上图中根据平衡因子来决定单双旋和左右旋转

AVL树构造和插入函数,测试函数:

这里的节点值类型简化为了int类型,原为 pair

#pragma once#include <iostream>using namespace std;#include <string>#include <array>#include <deque>#include <list>#include <map>#include <queue>#include <set>#include <stack>#include <unordered_map>#include <unordered_set>#include <vector>#include <algorithm>#include <assert.h>#include <windows.h>#include <ctime>namespace zhuandrong
{class AVLTreeNode{typedef AVLTreeNode Node;public:int _val;Node* _left;Node* _right;Node* _parent;int _bf;public:AVLTreeNode(const int val = 0): _val(val), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}};class AVLTree{typedef AVLTreeNode Node;protected:void RotateL(Node* parent){assert(parent);Node* cur = parent->_right;Node* cur_left = cur->_left;// 改变节点之间的关系parent->_right = cur_left;cur->_left = parent;// 改变_parent的指向//原本 parent 的 _parentNode* ppNode = parent->_parent;//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 curif (ppNode){if (ppNode->_left == parent){ppNode->_left = cur;}else{ppNode->_right = cur;}cur->_parent = ppNode;}//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根else{_root = cur;cur->_parent = nullptr;}if (cur_left){cur_left->_parent = parent;}parent->_parent = cur;// 更新平衡因子cur->_bf = parent->_bf = 0;}//void RotateL(Node* parent)//{//	Node* cur = parent->_right;//	Node* cur_left = cur->_left;//	parent->_right = cur_left;//	cur->_left = parent;//	Node* ppNode = parent->_parent;//	if (cur_left)//右孩子可能存在,也可能不存在,所以需要判断,需要在parent改变前判断//	{//		cur_left->_parent = parent;//	}//	parent->_parent = cur;//	if (parent == _root)//parent可能是根节点,也可能不是根节点//	{//		_root = cur;//		cur->_parent = nullptr;//	}//	else//	{//		if (ppNode->_left == parent)//		{//			ppNode->_left = cur;//		}//		else//		{//			ppNode->_right = cur;//		}//		cur->_parent = ppNode;//	}//	cur->_bf = parent->_bf = 0;//将平衡因子调整//}void RotateR(Node* parent){assert(parent);Node* cur = parent->_left;Node* cur_right = cur->_right;// 改变节点之间的关系parent->_left = cur_right;cur->_right = parent;// 改变_parent的指向//原本 parent 的 _parentNode* ppNode = parent->_parent;//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 curif (ppNode){if (ppNode->_left == parent){ppNode->_left = cur;}else{ppNode->_right = cur;}cur->_parent = ppNode;}//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根else{_root = cur;cur->_parent = nullptr;}if (cur_right){cur_right->_parent = parent;}parent->_parent = cur;// 更新平衡因子cur->_bf = parent->_bf = 0;}//void RotateR(Node* parent)//{//	Node* cur = parent->_left;//	Node* curRight = cur->_right;//	parent->_left = curRight;//	cur->_right = parent;//	Node* ppNode = parent->_parent;//	if (curRight)//右孩子可能存在,也可能不存在,所以需要判断,需要在parent改变前判断//	{//		curRight->_parent = parent;//	}//	parent->_parent = cur;//	if (parent == _root)//parent可能是根节点,也可能不是根节点//	{//		_root = cur;//		cur->_parent = nullptr;//	}//	else//	{//		if (ppNode->_left == parent)//		{//			ppNode->_left = cur;//		}//		else//		{//			ppNode->_right = cur;//		}//		cur->_parent = ppNode;//	}//	cur->_bf = parent->_bf = 0;//将平衡因子调整//}void RotateRL(Node* parent){Node* cur = parent->_right;Node* cur_left = cur->_left;int bf = cur_left->_bf;RotateR(parent->_right);RotateL(parent);/*parent->_bf = cur_left->_bf = 0;cur->_bf = flag;*/parent->_bf = 0;cur->_bf = 0;cur_left->_bf = 0;if (bf == -1){cur->_bf = 1;}if (bf == 1){parent->_bf = -1;}}void RotateLR(Node* parent){Node* cur = parent->_left;Node* cur_right = cur->_right;int bf = cur_right->_bf;RotateL(parent->_left);RotateR(parent);/*parent->_bf = parent->_parent->_bf = 0;parent->_parent->_left->_bf = -1;*/cur->_bf = 0;parent->_bf = 0;cur_right->_bf = 0;if (bf == 1){cur->_bf = -1;}if (bf == -1){parent->_bf = 1;}}protected:Node* _root;public:AVLTree(): _root(nullptr){}AVLTree(vector<int> v){for (auto& e : v){if (e == 8){int i = 0;}insert(e);assert(IsBalance());}}bool insert(int val){// 判断是否有根节点if (_root){//先找到合适的插入位置Node* cur = _root;Node* parent = nullptr;while (cur){if (val < cur->_val){parent = cur;cur = cur->_left;}else if (val > cur->_val){parent = cur;cur = cur->_right;}else{cout << "插入的元素值已重复" << endl;return false;}}cur = new Node(val);cur->_parent = parent;if (val < parent->_val){parent->_left = cur;}else{parent->_right = cur;}//对平衡因子进行更新while (parent){if (cur == parent->_left){--parent->_bf;}else if (cur == parent->_right){++parent->_bf;}//若 parent->_bf == 0,表明平衡因子调整结束if (!parent->_bf){break;}else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}//若parent->_bf == 2 && cur->_bf == 1,将parent进行左单旋else if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);break;}//若parent->_bf == 2 && cur->_bf == -1,将parent进行右左单旋else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);break;}//若parent->_bf == -2 && cur->_bf == -1,将parent进行右单旋else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);break;}//若parent->_bf == -2 && cur->_bf == 1,将parent进行左右单旋else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);break;}else{assert(false);}}}else{_root = new Node(val);}return true;}int TreeHight(Node* root){if (root == nullptr)return 0;int leftHight = TreeHight(root->_left);int rightHight = TreeHight(root->_right);return leftHight > rightHight ? leftHight + 1 : rightHight + 1;}void Inorder(){_Inorder(_root);}bool IsBalance(){return _IsBalance(_root);}private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_val << endl;_Inorder(root->_right);}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHight = TreeHight(root->_left);int rightHight = TreeHight(root->_right);//检查平衡因子对不对if (rightHight - leftHight != root->_bf){cout << "平衡因子出现异常" << endl;cout << rightHight - leftHight << endl;return false;}//需要递归检查是否平衡return (leftHight - rightHight <= 1 && leftHight - rightHight >= -1)&& _IsBalance(root->_left) && _IsBalance(root->_right);}};void test_AVLTree(){/*vector<int> v = { 6, 4, 32, 2, 7, 8 };AVLTree avl_tree(v);assert(avl_tree.IsBalance());*/AVLTree avl_tree;srand((unsigned int)time(NULL));for (int i = 1000; i >= 0; --i){avl_tree.insert(rand());//avl_tree.insert(i);assert(avl_tree.IsBalance());}//srand((unsigned int)time(0));//const size_t N = 10000;//for (size_t i = 0; i < N; ++i)//{//	size_t x = rand();//	avl_tree.insert(x);//	//cout << t.IsBalance() << endl;//}//avl_tree.Inorder();//cout << avl_tree.IsBalance() << endl;}}

红黑树

1. 红黑树的概念和性质(什么是红黑树,并且作为一颗红黑树的要求)

8f14b4f1c0434c04abce9efca47479a1.png

以上是红黑树的概念和性质,在红黑树里不仅仅要考虑 cur 、curleft 、 parent 还要引入 grandfather,具体为什么看到后面就知道了

2. 红黑树类的属性

是使用枚举枚举出 红 黑 两种情况

58533f47adb74e74bda140814c9b16bd.png

3. 红黑树的插入函数

还是分情况讨论:

4. 总结

根据性质可以分为以下情况:

可以看出第一种情况(uncle存在且为黑)只需要变色即可,在看是否继续向上调整

第二种情况就要旋转了,而如何旋转本质图中也详细说了,取决于 grandfather、parent、cur 之间的关系,其实AVL树的旋转的决定因素也在此,只是转换成平衡因子进行描述

关于红黑,具体的思想关键体现在插入函数中,复习请自行分析出两种大情况,和情况二的细分,然后写出插入函数,才算过关

总体代码:

#pragma once#include <iostream>using namespace std;#include <string>#include <array>#include <deque>#include <list>#include <map>#include <queue>#include <set>#include <stack>#include <unordered_map>#include <unordered_set>#include <vector>#include <algorithm>#include <assert.h>#include <windows.h>#include <ctime>namespace zhuandrong
{enum Color{RED,BLACK};template<typename K, typename T>class RBTreeNode{typedef RBTreeNode Node;public:pair<K, T> _t;Node* _left;Node* _right;Node* _parent;Color color;public:RBTreeNode(pair<K, T> t): _t(t), _left(nullptr), _right(nullptr), _parent(nullptr), color(RED){}};template<typename K, typename T>class RBTree{typedef RBTreeNode<K, T> Node;protected:Node* _root;protected:void RotateL(Node* parent){assert(parent);Node* cur = parent->_right;Node* cur_left = cur->_left;// 改变节点之间的关系parent->_right = cur_left;cur->_left = parent;// 改变_parent的指向//原本 parent 的 _parentNode* ppNode = parent->_parent;//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 curif (ppNode){if (ppNode->_left == parent){ppNode->_left = cur;}else{ppNode->_right = cur;}cur->_parent = ppNode;}//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根else{_root = cur;cur->_parent = nullptr;}if (cur_left){cur_left->_parent = parent;}parent->_parent = cur;}void RotateR(Node* parent){assert(parent);Node* cur = parent->_left;Node* cur_right = cur->_right;// 改变节点之间的关系parent->_left = cur_right;cur->_right = parent;// 改变_parent的指向//原本 parent 的 _parentNode* ppNode = parent->_parent;//若原本 ppNode(原本 parent 的 _parent)为空,说明parent原本不是根,现需把 ppNode 给到 cur 的 _parent,并且把 ppNode 的子更新为 curif (ppNode){if (ppNode->_left == parent){ppNode->_left = cur;}else{ppNode->_right = cur;}cur->_parent = ppNode;}//若原本 parent->_parent 为空,说明parent原本是根,现需把_root更新为根else{_root = cur;cur->_parent = nullptr;}if (cur_right){cur_right->_parent = parent;}parent->_parent = cur;}void RotateRL(Node* parent){RotateR(parent->_right);RotateL(parent);}void RotateLR(Node* parent){RotateL(parent->_left);RotateR(parent);}public:RBTree(): _root(nullptr){}RBTree(vector <pair<K, T>> v){for (auto& e : v){if (e.second == 5){int i = 0;}insert(e);}}bool insert(const pair<K, T> t){// 先判断根节点是否为空if (_root){// 若不为空,先找到合适的插入位置Node* cur = _root;Node* parent = nullptr;while (cur){if (t.second < cur->_t.second){parent = cur;cur = cur->_left;}else if (t.second > cur->_t.second){parent = cur;cur = cur->_right;}else{//cout << "该元素已经插入,请重试" << endl;return false;}}//找到该位置后进行插入并且链接关系cur = new Node(t);cur->_parent = parent;if (t.second < parent->_t.second){parent->_left = cur;}else{parent->_right = cur;}// 进行调整while (parent && parent->color == RED){Node* grandfather = parent->_parent;Node* uncle = nullptr;if (parent == grandfather->_left){uncle = grandfather->_right;}else{uncle = grandfather->_left;}// 第一种情况:如果uncle存在且为红if (uncle && uncle->color == RED){//将p和u变黑,g变红parent->color = uncle->color = BLACK;grandfather->color = RED;//更新,并且判断更新后的parent是否为根,不为根就继续调整;为根表明调整结束cur = grandfather;parent = cur->_parent;if (parent == _root){break;}}// 第二种情况:如果uncle存在且为黑色或者uncle不存在else{// 当 cur 为 parent 的左孩子时if (parent->_left == cur){if (grandfather->_left == parent){//       g//    p     u// c//Node* uncle = grandfather->_right;RotateR(grandfather);grandfather->color = cur->color = RED;parent->color = BLACK;}else{//      g             g                c//   u     p   --> u     c     -->  g     p//        c                 p      u//Node* uncle = grandfather->_left;RotateRL(grandfather);grandfather->color = parent->color = RED;cur->color = BLACK;}}// 当 cur 为 parent 的右孩子时else{if (grandfather->_right == parent){//    g     // u     p     //        c//Node* uncle = grandfather->_right;RotateL(grandfather);grandfather->color = cur->color = RED;parent->color = BLACK;}else{//      g              g                c//   p     u   -->  c     u     -->  p     g//    c            p                         u//Node* uncle = grandfather->_left;RotateLR(grandfather);grandfather->color = parent->color = RED;cur->color = BLACK;}}break;}}}// 若为空,则该元素作为根节点else{_root = new Node(t);}_root->color = BLACK;return true;}bool judge_color(Node* root, int blacksum, int blacknum){if (!root){if (blacknum != blacksum){return false;}return true;}if (root->color == BLACK){++blacknum;}if (root->color == RED && root->_parent && root->_parent->color == RED){return false;}return judge_color(root->_left, blacksum, blacknum)&& judge_color(root->_right, blacksum, blacknum);}bool isBalance(){if (!_root){cout << "根为空" << endl;return false;}if (_root->color == RED){cout << "根的颜色为红色,非法" << endl;return false;}Node* cur = _root;int blacksum = 0;while (cur){if (cur->color == BLACK){++blacksum;}cur = cur->_right;}return judge_color(_root, blacksum, 0);}};void test_RBTree(){/*vector<pair<int, int>> v;v.push_back(make_pair(1, 13));v.push_back(make_pair(2, 8));v.push_back(make_pair(3, 17));v.push_back(make_pair(4, 1));v.push_back(make_pair(5, 11));v.push_back(make_pair(6, 15));v.push_back(make_pair(7, 25));v.push_back(make_pair(8, 6));v.push_back(make_pair(9, 22));v.push_back(make_pair(10, 27));v.push_back(make_pair(11, 5));RBTree<int, int> rb_tree(v);cout << rb_tree.isBalance() << endl;*/RBTree<int, int> rb_tree;srand((unsigned int)time(NULL));for (int i = 0; i < 1000; ++i){rb_tree.insert(make_pair(i, rand()));}if (rb_tree.isBalance()){cout << "本次测试成功" << endl;}else{cout << "本次测试失败" << endl;}}}

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

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

相关文章

框架篇

一、Spring中的单例Bean是线程安全的吗 二、AOP相关面试题 三、Spring中的事务 四、Spring中事务失效的场景有 五、Spring bean的生命周期 六、Spring的循环依赖 七、SpringMVC的执行流程 八、自动配置原理 九、Spring框架常见的注解 十、Mybatis的执行流程 十一、MyBatis延迟加…

(转)tinymce-vue使用教程

一、资源下载 npm install tinymce -S //当前版本^5.1.1 npm install tinymce/tinymce-vue -S //当前版本^3.0.1二、安装语言包 资源下载后,在 node_modules 中找到 tinymce/skins 目录&#xff0c;然后将 skins 目录拷贝到 static 目录下,(PS: 如果是使用 vue-cli 3.x 构建…

前端开发tips

vue配置启动项目自动打开浏览器 打开package.json找到启动命令npm run dev 跟npm run serve(这两种命令都可以) 后面增加 --open Vue项目设置路径src目录别名为 Vue2 编辑vue.config.js内容如下&#xff1a; const { defineConfig } require(vue/cli-service)const path…

STM32Cube高效开发教程<基础篇>(六)----FSMC连接TFT-LCD屏

声明:本人水平有限,博客可能存在部分错误的地方,请广大读者谅解并向本人反馈错误。    本专栏博客参考《STM32Cube高效开发教程(基础篇)》,有意向的读者可以购买正版书籍辅助学习,本书籍由王维波老师、鄢志丹老师、王钊老师倾力打造,书籍内容干货满满。 一、 FSMC连接…

LeetCode34 在排序数组中寻找元素的第一个和最后一个位置

题目&#xff1a; 思路&#xff1a; https://blog.csdn.net/wangjiaqi333/article/details/124526112 直观的思路肯定是从前往后遍历一遍。用两个变量记录第一次和最后一次遇见target的下标&#xff0c;但这个方法的时间复杂度为O(n)&#xff0c;没有利用到数组升序排列的条件…

小程序-uni-app:将页面(html+css)生成图片/海报/名片,进行下载 保存到手机

一、需要描述 本文实现&#xff0c;uniapp微信小程序&#xff0c;把页面内容保存为图片&#xff0c;并且下载到手机上。 说实话网上找了很多资料&#xff0c;但是效果不理想&#xff0c;直到看了一个开源项目&#xff0c;我知道可以实现了。 本文以开源项目uniapp-wxml-to-can…

前端技术-并发请求

并发请求 代码解释 定义了一个函数 concurRequest&#xff0c;用于并发请求多个 URL 并返回它们的响应结果。 function concurRequest(urls, maxNum) {return new Promise((resolve, reject) > {if (urls.length 0) {resolve([]);return;}const results [];let index …

【vue2高德地图api】01-创建应用,获取key值

系列文章目录 【vue2高德地图api】视频效果&#xff08;手机端&#xff09;先看这里 文章目录 系列文章目录前言创建key&#xff08;2个&#xff09;1.1进入控制台1.2进入应用1.3 创建应用1.4输入名称和类型2.1 添加key2.2 选择对应信息2.3 创建js key和服务端 key 总结 前言 …

Qt/C++编写物联网组件/支持modbus/rtu/tcp/udp/websocket/mqtt/多线程采集

一、功能特点 支持多种协议&#xff0c;包括Modbus_Rtu_Com/Modbus_Rtu_Tcp/Modbus_Rtu_Udp/Modbus_Rtu_Web/Modbus_Tcp/Modbus_Udp/Modbus_Web等&#xff0c;其中web指websocket。支持多种采集通讯方式&#xff0c;包括串口和网络等&#xff0c;可自由拓展其他方式。自定义采…

排序算法-基数排序法(RadixSort)

排序算法-基数排序法&#xff08;RadixSort&#xff09; 1、说明 基数排序法与我们之前讨论的排序法不太一样&#xff0c;并不需要进行元素之间的比较操作&#xff0c;而是属于一种分配模式排序方式。 基数排序法比较的方向可分为最高位优先&#xff08;Most Significant Di…

支持语音与视频即时通讯项目杂记(一)

第一部分解释服务端的实现。 &#xff08;服务端结构&#xff09; 下面一个用于实现TCP服务器的代码&#xff0c;包括消息服务器&#xff08;TcpMsgServer&#xff09;和文件中转服务器&#xff08;TcpFileServer&#xff09;。 首先&#xff0c;TcpServer是TcpMsgServer和Tcp…

上采样相关技术

一、参考资料 上采样和上卷积的区别 怎样通俗易懂地解释反卷积&#xff1f; 卷积和池化的区别、图像的上采样&#xff08;upsampling&#xff09;与下采样&#xff08;subsampled&#xff09; [读论文]用全卷积Res网络做深度估计 对抗生成网络GAN系列——DCGAN简介及人脸图像生…

MapReduce任务个数如何影响执行效率?性能优化从这里做起

在正文开始之前&#xff0c;请先来回答一下这个问题&#xff1a; 题目&#xff1a;输入为3个文件&#xff0c;a.txt 300MB,b.txt 100MB,c.txt 58.MB&#xff0c;使用MapReduce的example程序&#xff0c;计算Wordcount&#xff0c;请问&#xff0c;应该有多少个MapTask&#xf…

算法、推理、部署,面了40多个大佬的感想

今年三月份到现在陆陆续续面了40来个人&#xff0c;有实习生&#xff0c;有校招生&#xff0c;也有来社招的大佬们。面了挺久&#xff0c;有些总结和感想&#xff0c;发出来和大家交流交流&#xff0c;也趁着这个机会为之后参与校招的同学提供一些学习方向。 我面的岗位主要是…

计算机网络第2章-CDN(4)

视频流和内容分发网 HTTP流和DASH 在HTTP流中&#xff0c;视频只是存储在HTTP服务器中作为一个普通的文件&#xff0c;每个文件有有一个特定的URL。当用户要看视频时&#xff0c;客户与服务器之间创建一个TCP连接并发送HTTP GET请求。 HTTP流具有严重缺陷&#xff0c;即所有…

quartz中jdbc.initialize-schema

never&#xff1a;从不进行初始化&#xff0c;也就是不清空数据库 always&#xff1a;每次都清空数据库进行初始化 embedded&#xff1a;只初始化内存数据库&#xff08;默认值&#xff09;

【扩散模型从原理到实战】Chapter2 Hugging Face简介

文章目录 Hugging Face的核心功能介绍Hugging Face开源库Hugging Face开源库Gradio工具介绍参考资料 Hugging Face是机器学习从业者协作和交流的平台&#xff0c;成立于2016年&#xff0c;在纽约和巴黎设有办事处&#xff0c;团队成员来自世界各地&#xff0c;远程办公。 致力于…

基于SSM的旅游信息管理系统设计与实现

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

堆排序代码模板

#include<iostream> using namespace std; const int N 1e5 9; int h[N], n, m, Size;//小根堆//u表示三个点中的根节点 void down(int u) {int t u;//设t为三个点中最小的那个点//如果左儿子存在并且小于根节点就将左儿子赋值给tif (u * 2 < Size && h[u …

什么是著作权?对此你了解多少?

在当今信息爆炸的时代&#xff0c;著作权成为一个备受关注的话题。创作是人类文明的重要组成部分&#xff0c;而著作权是创作者对自己作品的劳动和智慧的一种保护。很多人还不太了解著作权&#xff0c;那么希望看完此文&#xff0c;你会对它有一个新的认识。 一、著作权的概念 …