红黑树封装map和set
文章目录
- 红黑树封装map和set
- 1. 改良红黑树
- 1.1 改良后的节点
- 1.2 改良后的类
- 分别添加仿函数
- 代码
- 3. 封装map和set
- 3.1 set
- 3.2 map
- 3. 迭代器
- 3.1 begin 和 end
- 3.2 operator++()和operator--()
- 3.3 const迭代器
- set的迭代器
- map的迭代器
- 4. map的operator[]的重载
- 5. 完整代码实现
- 5.1 RBTree.h
- 5.2 set.h
- 5.3 map.h
红黑树封装map和set
1. 改良红黑树
如何改良,可以参考stl源码

我们先看库中,map和set的大框架, 发现set在底层红黑树存储的是<k,k>的键值对,不像我们平时认知以为只存储一个k,map在底层红黑树存储的是<k,v>的键值对。继续看库中红黑树的源码

发现在红黑树节点这里并不是我们之前实现的K, V形式,而是只给了一个Value形式。
对于set第二个模板参数是K,其节点中存储的就是K,这里的Value就代表的是K;
对于map第二个模板参数是pair<const K,V>,其节点中存储的就是pair<const K,V>,这里的Value就代表的是pair<const K,V>;
那么在红黑树模板参数设计时,为什么要多设计一个Key的模板参数呢? 是为了拿到单独的K类型,find和erase这些接口函数的参数是K;第二个模板参数才真正决定节点里面存储什么
1.1 改良后的节点
template<class T>
struct RBTreeNode //三叉链
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED) //默认颜色给红色{}
};
1.2 改良后的类
分别添加仿函数
插入和查找的过程中的比较不能想红黑树一样实现成kv.first的形式,我们要实现成用节点中实际存储的类型去比较,对于set存储的是K,可以直接去比较;对于map存储的是pair,比较时是用pair比较,但是pair的比较并不是我们想要的,我们还是想要pair中的K比较

上面的问题我们可以看库中源码,发现多给了一个模板参数KeyOfValue,其目的是取出K,可以分别给具体的仿函数来实现。

set这里直接返回key就行
struct SetKeyOfT
{const K &operator()(const K &key){return key;}
};
map这里需要返回pair的first, 即K
struct MapKeyOfT
{const K &operator()(const pair<const K, V> &kv){return kv.first;}
};
那么为什么需要这么做,对于set来说直接比较不就行了吗,当然可以,但是map并不是直接用_data类型比较(即pair本身), set为了和map复用同一棵红黑树,所以要实现成这种形式

同样我们插入和查找时也需要修改,用map和set传过来的仿函数定义出一个对象kot,帮助我们取出帮助我们取出_data中的Key。
代码
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}Node* Find(const K& key){Node* cur = _root;KeyOfT kot; //仿函数定义出对象,帮助我们取出_data中的Keywhile (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) >key){cur = cur->_left;}else{return cur; }}return nullptr;}bool Insert(const T& data){//第一次插入if (_root == nullptr){_root = new Node(data);_root->_col = BLACK; //根节点给黑色return true;}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return false; //该元素已经在树中存在了, 无法插入}}//链接cur = new Node(data);Node* newnode = cur; //cur可能会变色, 需要提前记录curif (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//处理红黑树颜色while (parent && parent->_col==RED){Node* grandfather = parent->_parent;//找叔叔 => 看父亲在祖父的哪边if (grandfather->_left == parent) {Node* uncle = grandfather->_right;//3种情况//情况1: u存在且为红, 变色处理, 并继续向上处理//变色: p,u变黑, g变红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur=grandfather;parent = cur->_parent;}else //情况2+3: u不存在/u存在且为黑, 旋转 + 变色{// g// p u// cif(cur==parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// p u// cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else //(grandfather->_right == parent){Node* uncle = grandfather->_left;//3种情况//情况1: u存在且为红, 变色处理, 并继续向上处理//变色: p,u变黑, g变红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else //情况2+3: u不存在/u存在且为黑, 旋转 + 变色{// g// u p// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK; //根节点是黑色的return true;}
private:Node* _root = nullptr;
};
3. 封装map和set
3.1 set
namespace yj
{template<class K>class set{//作用是:将T中的key提取出来struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:bool insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}
3.2 map
namespace yj
{template<class K, class V>class map{//作用是:将T中的key提取出来struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:bool insert(const pair<const K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}
3. 迭代器
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:
3.1 begin 和 end
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,STL是将end()放在头结点的位置:我们上次设计的RBTree没有头结点这个结构,因此我们也就不与STL的实现方式完全一样,end()就直接设置为nullptr。
iterator begin() // 中序遍历第一个
{Node *cur = _root;while (cur && cur->_left) //找最左侧节点{cur = cur->_left;}return iterator(cur); // 节点指针构造的迭代器
}iterator end() // 最后一个节点的下一个
{return iterator(nullptr);
}
3.2 operator++()和operator–()
++的实现思路:
-
右不为空, 下一个就是右子树的最左节点
-
右为空, 沿着到根的路径, 找孩子是父亲左的那个祖先
Self &operator++()
{if (_node->_right){// 1. 右不为空, 下一个就是右子树的最左节点Node *subLeft = _node->_right;while (subLeft->_left){subLeft = subLeft->_left;}_node = subLeft; // 将节点的地址赋值即可}else{// 2. 右为空, 沿着到根的路径, 找孩子是父亲左的那个祖先Node *cur = _node;Node *parent = _node->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;
}
–的实现思路: 与++相反
-
左不为空, 下一个就是左子树的最右节点
-
左为空, 沿着到根的路径, 找孩子是父亲右的那个祖先
Self &operator--()
{if (_node->_left){// 1. 左不为空, 下一个就是左子树的最右节点Node *subRight = _node->_left;while (subRight->_right){subRight = subRight->_right;}_node = subRight; // 将节点的地址赋值即可}else{// 2. 左为空, 沿着到根的路径, 找孩子是父亲右的那个祖先Node *cur = _node;Node *parent = _node->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;
}
3.3 const迭代器
如果是const迭代器,那可以在迭代器类中多加上两个模板参数:T&, T*偏特化,当然实际上是Ref,Ptr的全特化;
那如何实现set的const迭代器呢?由于set不能修改普通迭代器就是const迭代器,const迭代器还是const迭代器;
对于map来说普通迭代器就是普通迭代器,const迭代器就是const迭代器,但是map的pair中的value是可以修改的,因此我们就需要在RBTree中把普通迭代器和const迭代器均实现出来,同时也要支持从普通迭代器 隐式类型转换 成构造const迭代器的构造函数。,当map的迭代器类模板被实例化成普通迭代器时,它就是拷贝构造;迭代器类模板被实例化成const迭代器时,它是一个支持用普通迭代器构造初始化const迭代器的构造函数。

template <class T, class Ref, class Ptr>
struct _RBTreeIterator
{typedef RBTreeNode<T> Node;typedef _RBTreeIterator<T, Ref, Ptr> Self;Node *_node;_RBTreeIterator(Node *node): _node(node){}// 1. typedef _RBTreeIterator<T, T&, T*> iterator; 拷贝构造//// 支持普通迭代器构造const迭代器的构造函数// 2. typedef _RBTreeIterator<T, const T&, const T*> const_iterator;// 支持从普通迭代器 隐式类型转换 成构造const迭代器的构造函数_RBTreeIterator(const _RBTreeIterator<T, T &, T *> &it): _node(it._node){}
}
set的迭代器
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin()
{return _t.begin();
}iterator end()
{return _t.end();
}const_iterator begin()const
{return _t.begin();
}const_iterator end()const
{return _t.end();
}
map的迭代器
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin()
{return _t.begin();
}iterator end()
{return _t.end();
}const_iterator begin()const
{return _t.begin();
}const_iterator end()const
{return _t.end();
}
4. map的operator[]的重载
根据map的[]性质: 返回make_pair<iterator,bool>的first,解引用找到节点value, 借助Insert函数,同时需要将我们插入函数返回类型的从bool变成pair类型
V& operator[](const K &key)
{pair<iterator, bool> ret = _t.Insert(make_pair(key, V())); //V()构造一个匿名对象return ret.first->second; // 找到ret(make_pair<iterator,bool>)的first,解引用找到节点value
}
pair<iterator, bool> Insert(const T &data)
{// 第一次插入if (_root == nullptr){_root = new Node(data);_root->_col = BLACK; // 根节点给黑色return make_pair(iterator(_root), true);}KeyOfT kot;Node *parent = nullptr;Node *cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(cur), false); // 该元素已经在树中存在了, 无法插入}}// 链接cur = new Node(data);Node *newnode = cur; // cur可能会变色, 需要提前记录curif (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;// 处理红黑树颜色while (parent && parent->_col == RED){Node *grandfather = parent->_parent;// 找叔叔 => 看父亲在祖父的哪边if (grandfather->_left == parent){Node *uncle = grandfather->_right;// 3种情况// 情况1: u存在且为红, 变色处理, 并继续向上处理// 变色: p,u变黑, g变红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续向上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3: u不存在/u存在且为黑, 旋转 + 变色{// g// p u// cif (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// p u// cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else //(grandfather->_right == parent){Node *uncle = grandfather->_left;// 3种情况// 情况1: u存在且为红, 变色处理, 并继续向上处理// 变色: p,u变黑, g变红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续向上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3: u不存在/u存在且为黑, 旋转 + 变色{// g// u p// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK; // 根节点是黑色的return make_pair(iterator(newnode), true);
}
5. 完整代码实现
5.1 RBTree.h
#include<iostream>
#include<utility>
#include<assert.h>
#include<stdlib.h>
using namespace std;//节点的颜色
enum Colour
{RED,BLACK,
};template<class T>
struct RBTreeNode //三叉链
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED) //默认颜色给红色{}
};template<class T,class Ref, class Ptr>
struct _RBTreeIterator
{typedef RBTreeNode<T> Node;typedef _RBTreeIterator<T,Ref,Ptr> Self;Node* _node;_RBTreeIterator(Node* node):_node(node){}// 1. typedef _RBTreeIterator<T, T&, T*> iterator; 拷贝构造// // 支持普通迭代器构造const迭代器的构造函数// 2. typedef _RBTreeIterator<T, const T&, const T*> const_iterator; //支持从普通迭代器 隐式类型转换 成构造const迭代器的构造函数_RBTreeIterator(const _RBTreeIterator<T, T&, T*>&it):_node(it._node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_right){//1. 右不为空, 下一个就是右子树的最左节点Node* subLeft = _node->_right;while (subLeft->_left){subLeft = subLeft->_left;}_node = subLeft; //将节点的地址赋值即可}else{//2. 右为空, 沿着到根的路径, 找孩子是父亲左的那个祖先Node* cur = _node;Node* parent = _node->_parent;while (parent && cur==parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node->_left){//1. 左不为空, 下一个就是左子树的最右节点Node* subRight = _node->_left;while (subRight->_right){subRight = subRight->_right;}_node = subRight; //将节点的地址赋值即可}else{//2. 左为空, 沿着到根的路径, 找孩子是父亲右的那个祖先Node* cur = _node;Node* parent = _node->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};//仿函数
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}typedef _RBTreeIterator<T, T&, T*> iterator;typedef _RBTreeIterator<T, const T&, const T*> const_iterator;iterator begin() //中序遍历第一个{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur); //节点指针构造的迭代器}iterator end() //最后一个节点的下一个{return iterator(nullptr); }const_iterator begin()const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur); }const_iterator end()const {return const_iterator(nullptr);}Node* Find(const K& key){Node* cur = _root;KeyOfT kot; //仿函数定义出对象,帮助我们取出_data中的Keywhile (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) >key){cur = cur->_left;}else{return cur; }}return nullptr;}pair<iterator,bool> Insert(const T& data){//第一次插入if (_root == nullptr){_root = new Node(data);_root->_col = BLACK; //根节点给黑色return make_pair(iterator(_root), true);}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(cur), false); //该元素已经在树中存在了, 无法插入}}//链接cur = new Node(data);Node* newnode = cur; //cur可能会变色, 需要提前记录curif (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//处理红黑树颜色while (parent && parent->_col==RED){Node* grandfather = parent->_parent;//找叔叔 => 看父亲在祖父的哪边if (grandfather->_left == parent) {Node* uncle = grandfather->_right;//3种情况//情况1: u存在且为红, 变色处理, 并继续向上处理//变色: p,u变黑, g变红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur=grandfather;parent = cur->_parent;}else //情况2+3: u不存在/u存在且为黑, 旋转 + 变色{// g// p u// cif(cur==parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// p u// cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else //(grandfather->_right == parent){Node* uncle = grandfather->_left;//3种情况//情况1: u存在且为红, 变色处理, 并继续向上处理//变色: p,u变黑, g变红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else //情况2+3: u不存在/u存在且为黑, 旋转 + 变色{// g// u p// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK; //根节点是黑色的return make_pair(iterator(newnode), true);}void Inorder(){_Inorder(_root);}int Height(){return _Height(_root);}bool IsBalance() //重点检查规则{//先检查根节点if (_root && _root->_col == RED){cout << "根节点颜色是红色" << endl;return false;}int benchmark = 0; //基准值Node* cur = _root;while(cur) //走最左路径{if (cur->_col == BLACK)++benchmark;cur = cur->_left;}//连续红色节点return _Check(_root, 0,benchmark);}private://左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppnode == nullptr) //parent本身就是根{_root = subR;_root->_parent = nullptr;}else //parent只是一棵子树{if (ppnode->_left == parent) //判断原来的节点是左右哪一棵子树{ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (ppnode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}int _Height(Node* root){if (root == nullptr)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}bool _Check(Node* root, int blackNum, int benchmark) //基准值{if (root == nullptr){if (benchmark != blackNum){cout << "某条路径黑色节点的数量不相等" << endl;return false;}return true;}//DFS检查黑色节点数量if (root->_col == BLACK){++blackNum;}//反向检查 ---> 红色节点不能连续if (root->_col == RED&& root->_parent&& root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return _Check(root->_left, blackNum, benchmark)&& _Check(root->_right, blackNum, benchmark);}void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << " ";_Inorder(root->_right);}Node* _root = nullptr;
};
5.2 set.h
#include"RBTree.h"namespace yj
{template<class K>class set{//作用是:将T中的key提取出来struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}
5.3 map.h
#include"RBTree.h"namespace yj
{template<class K, class V>class map{//作用是:将T中的key提取出来struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public://取类模板的内嵌类型前需要添加typename, 因为编译器无法区分取到的是内嵌类型还是静态变量//加typename告诉编译器这是类模板的内嵌类型typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}V& operator[](const K& key){pair<iterator,bool> ret= _t.Insert(make_pair(key, V()));return ret.first->second; //找到ret(make_pair<iterator,bool>)的first,解引用找到节点value}pair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}
相关文章:
红黑树封装map和set
文章目录 红黑树封装map和set1. 改良红黑树1.1 改良后的节点1.2 改良后的类分别添加仿函数代码 3. 封装map和set3.1 set3.2 map 3. 迭代器3.1 begin 和 end3.2 operator()和operator--()3.3 const迭代器set的迭代器map的迭代器 4. map的operator[]的重载5. 完整代码实现5.1 RBT…...
python序列
在Python中,序列类型包括字符串、列表、元组、集合和字典,这些序列支持以下几种通用的操作,但比较特殊的是,集合和字典不支持索引、切片、相加和相乘操作。 字符串也是一种常见的序列,它也可以直接通过索引访问字符串内…...
LeetCode35. 搜索插入位置(二分法入门)
写在前面: 题目链接:LeetCode35. 搜索插入位置 编程语言:C 题目难度:简单 一、题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会…...
macOS Ventura 13.4 RC3(22F66)发布
系统介绍 5 月 17 日消息,苹果今日向 Mac 电脑用户推送了 macOS 13.4 RC 3 更新(内部版本号:22F66),本次更新距离上次发布隔了 5 天。 macOS Ventura 带来了台前调度、连续互通相机、FaceTime 通话接力等功能。其中&…...
CSI和DSI介绍
1、CSI和DSI的接触协议介绍 MIPI、CSI、CCI 协议基础介绍_csi协议_赵哈哈x的博客-CSDN博客 流媒体技术基础-摄像头接口与标准_【零声教育】音视频开发进阶的博客-CSDN博客 《摄像头 —— MIPI CSI-2简介》 USB摄像头使用 — Lichee zero 文档 2、AIO-3288J ,and…...
vue3+antDesignVue前端纯导出
效果 <a-buttonsize"default"style"margin-left: 10px"click"exportData">导出</a-button>1.下载所需依赖 npm install xlsx --save npm install file-saver --save<script setup> import { reactive, ref } from "vue…...
卷积神经网络的剪枝及其在嵌入式视觉系统中的应用
卷积神经网络的剪枝及其在嵌入式视觉系统中的应用 摘要 在过去的十年里,计算机视觉的最新技术一直是由深度神经网络,特别是卷积神经网络所控制的。无论是分类、语义分割还是目标检测,神经网络现在都是一个无可争议的首选。因此,…...
Spring IOC - Bean的初始化
在bean的初始化阶段,bean已经被实例化及属性填充了,此时的bean已相对成熟,接下来的初始化阶段还会做一些额外的工作对bean做进一步处理,主要包括以下四个方面: 调用aware接口方法 初始化前:调用初始化前的…...
Golang 安装
学习目标 本文旨在帮助初学者了解 Golang 在不同环境下的安装方法,并提供相关资源和参考链接。 学习内容 安装 Golang on Windows 下载 Golang for Windows 安装包,地址:https://golang.org/dl/双击运行安装包,按照提示完成安装。配置环境变量: 在系统变量中添加 GOROO…...
( 位运算 ) 338. 比特位计数 ——【Leetcode每日一题】
❓338. 比特位计数 难度:简单 给你一个整数 n ,对于 0 < i < n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n 1 的数组 ans 作为答案。 示例 1: 输入:n 2 输出:[0,1,…...
Unity之新版输入系统InputSystem入门
一.前言 最近Unity版本全面升级到Unity2021了,着色器也全面使用URP,接下来毫无疑问,输入系统也要全面升级InputSystem,这也是Unity官方希望我们做的,毕竟现在都2023年了,我们的技术是需要跟上时代的步伐。 二.新老版本区别 老版本的InputSysten系统,其实就一个Input静…...
python 之 logging的使用
一、日志模块 import logginglogging.debug("调试日志") logging.info(消息日志) logging.warning("告警日志") logging.error(错误日志) logging.critical(严重错误日志)debug(调试)级别用于输出调试信息,这些信息主…...
gunicorn常用参数命令
Gunicorn 是一个 Python 的 WSGI HTTP 服务器。具有实现简单,轻量级,高性能等特点。更多介绍内容参考官网,这里介绍几个常用参数。 安装 pip3 install gunicorn通过输入gunicorn -v查看版本。 最简洁的启动。首先进入到项目目录,例如django项目和mana…...
TimerResolution.exe
TimerResolution.exe是一款常用的Windows实用程序,用于调整系统计时器的分辨率。它提供了一种简便的方法,让用户能够更精确地控制计时器的运行方式,从而改善系统的性能和响应时间。无论是进行游戏、音频处理还是其他需要精确计时的任务,TimerResolution.exe都能提供极大的帮…...
Qt魔法书:打造自定义鼠标键盘脚本
Qt魔法书:打造自定义鼠标键盘脚本 一、引言(Introduction)1.1 脚本的重要性(Importance of Scripts)1.2 Qt在脚本制作中的优势(Advantages of Qt in Script Making)1.3 文章目标与结构概述&…...
〖Python网络爬虫实战㉖〗- Selenium库和ChromeDriver驱动的安装
订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 说明:本专栏持续更新中,目前专栏免费订阅,在转为付费专栏前订阅本专栏的,可以免费订阅付…...
U8产成品入库API接口 --参照生产订单/产品检验/不良品
//第二步:构造环境上下文对象,传入login,并按需设置其它上下文参数 U8EnvContext envContext new U8EnvContext(); envContext.U8Login u8Login; //第三步:设置API地址标识(Url) …...
gdb打印的堆栈有些函数是??()是什么
当 gdb 打印的堆栈中出现 ??() 格式的函数名时,通常表示对应的函数名无法被解析,这可能是由以下几种原因导致的: 缺少符号表信息:如果程序的可执行文件没有包含符号表信息,或者 gdb 没有加载符号表信息,就…...
【Jmeter第三章】Jmeter给请求添加请求头
给请求加上请求头最常见的场景就是在请求头上添加token了,这里也拿添加token来举例 1、添加某个请求的请求头 1、选中HTTP请求,右键添加 2、添加请求头 2、添加公共的请求头信息 其实步骤和上面是一样的,只不过是选择:线程组…...
WebApi必须知道的RestFul,Swagger,OAuth2.0
什么是RestFul RestFul是一种软件架构风格,它是基于HTTP协议设计的。它是一种轻量级的、简单易懂、易于维护的架构风格,主要用于Web服务的设计。它的设计原则是面向资源,每个资源都有唯一的标识符,客户端通过HTTP协议对这些资源进…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
