红黑树封装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协议对这些资源进…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...