C++:基于红黑树封装map和set

目录

红黑树的修改

红黑树节点

红黑树结构

红黑树的迭代器

红黑树Insert函数

红黑树的默认成员函数

修改后完整的红黑树 

set、map的模拟实现

set

map

测试封装的set和map


红黑树的修改

想要用红黑树封装map和set,需要对之前实现的key-value红黑树进行修改,因为map是key-value结构而set是key结构之前实现的红黑树不能满足需求

我们需要将key和key-value抽象统一成成一个类型T,需要修改红黑树节点类红黑树类进行。

红黑树节点

enum Color
{RED,BLACK
};//T代表set传过来的key或者map传过来的(pair)key-value
template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr){}
};

红黑树结构

template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public://...private:Node* _root = nullptr;
};

3个模板参数的解释:

1.对于K代表的是key的类型,无论你是set和map,key的类型都是需要传入的,因为在Find函数的参数需要用到key的类型,如果是set,K和T都代表key的类型,第一个模板参数可有可没有,但是对于map来说,T代表的key-value类型(pair)没有第一个参数的话就无法拿到key的类型,从而无法实现Find函数。

2.对于T,代表的是红黑树里存的是key还是key-value

3.对于KeyOfT,这个参数其实是一个仿函数(对一个struct类重载  () ),这个仿函数是为了拿到T里面的key的具体值,因为Insert函数涉及到key的比较如果是map的话,T是key-value,拿不到具体的key值,就无法实现Insert函数,对于set的话,KeyOfT是可有可没有的。

红黑树的迭代器

红黑树的迭代器是对红黑树节点指针的封装,其实现与list的迭代器类似,但是由于红黑树的遍历走的是中序遍历,所以其迭代器的++走的是当前节点中序遍历的下一个节点,其--也是类似的道理。所以想要实现迭代器关键是实现++和--,其余部分与list的迭代器类似。

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;Node* _root;RBTreeIterator(Node* node, Node* root):_node(node),_root(root){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}};

实现红黑树中序遍历++

实现++的核心就是只关注局部逻辑而不看全局,只考虑中序遍历的下一个节点

迭代器it++时

  • 1.如果it指向的当前节点的右子树不为空,则去找当前节点的右子树的最左节点。
  • 2.如果it指向的当前节点的右子树为空,则去向上寻找一个特殊节点,该特殊节点是其父亲的左节点,此时特殊节点的父亲就是当前节点中序遍历的下一个节点

 上述逻辑是根据二叉树中序遍历总结而来。

	//前置++,返回++之后的值Self operator++(){// 当前节点的右子树不为空,中序的下一个节点是// 当前节点的右子树的最左(最小)节点if (_node->_right){Node* rightMin = _node->_right;while (rightMin->_left)rightMin = rightMin->_left;_node = rightMin;}//当前节点的右子树为空,说明当前节点所属的子树已经中序遍历访问完毕//往上寻找中序遍历的下一个节点,找到一个节点是父亲的左节点的特殊节点//此时特殊节点的父亲就是当前节点中序遍历的下一个节点else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//后置++Self operator++(int){Self tmp = *this;++(*this);return tmp;}

由于有关迭代器的函数 begin() 和 end() 是左闭右开区间,这里我们就用nullptr作为 end()这里的++逻辑也是可以兼顾到 end()。

  • 假设当前节点是中序遍历的最后一个节点,也就是整棵树的最右节点,其右子树为空,向上寻找的过程中,找不出满足条件的特殊节点(根节点的父亲是nullptr),parent为空时退出循环,给_node赋值,还是找到了当前节点中序遍历的下一个节点。

实现红黑树中序遍历--

这与++的逻辑是相反的,也可以当成反中序遍历右根左的++。

//前置--
Self operator--()
{//处理end()的情况//--end()得到的应该是中序遍历的最后一个节点//整一棵树的最右节点if (_node == nullptr){Node* rightMost = _root;while (rightMost && rightMost->_right)rightMost = rightMost->_right;_node = rightMost;}//当前节点的左子树不为空,找左子树的最右节点else if (_node->_left){Node* leftMost = _node->_left;while (leftMost && leftMost->_right)leftMost = leftMost->_right;_node = leftMost;}//当前节点的左子树为空else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}//后置--
Self operator--(int)
{Self tmp = *this;--(*this);return tmp;
}

实现迭代器的完整代码

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;Node* _root;RBTreeIterator(Node* node, Node* root):_node(node),_root(root){}//前置++,返回++之后的值Self operator++(){// 当前节点的右子树不为空,中序的下一个节点是// 当前节点的右子树的最左(最小)节点if (_node->_right){Node* rightMin = _node->_right;while (rightMin->_left)rightMin = rightMin->_left;_node = rightMin;}//当前节点的右子树为空,说明当前节点所属的子树已经中序遍历访问完毕//往上寻找中序遍历的下一个节点,找到一个节点是父亲的左节点的特殊节点//此时特殊节点的父亲就是当前节点中序遍历的下一个节点else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//后置++Self operator++(int){Self tmp = *this;++(*this);return tmp;}//前置--Self operator--(){//处理end()的情况//--end()得到的应该是中序遍历的最后一个节点//整一棵树的最右节点if (_node == nullptr){Node* rightMost = _root;while (rightMost && rightMost->_right)rightMost = rightMost->_right;_node = rightMost;}//当前节点的左子树不为空,找左子树的最右节点else if (_node->_left){Node* leftMost = _node->_left;while (leftMost && leftMost->_right)leftMost = leftMost->_right;_node = leftMost;}//当前节点的左子树为空else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//后置--Self operator--(int){Self tmp = *this;--(*this);return tmp;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}};

红黑树Insert函数

对Insert函数的修改,主要修改其返回值和key的比较逻辑返回值应改为pair<Iterator, bool>key的比较逻辑用KeyOfT实例化出来的对象比较即可

pair<Iterator, bool> Insert(const T& data)
{//按二叉搜索树插入if (_root == nullptr){_root = new Node(data);//根节点为黑色_root->_col = BLACK;//return pair<Iterator, bool>(Iterator(_root, _root), true);return { Iterator(_root, _root), true };}//仿函数,获取T中的具体的keyKeyOfT 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;}elsereturn { Iterator(cur, _root), false };}cur = new Node(data);Node* newnode = cur;//非空树插入红色节点cur->_col = RED;//判断cur应该插入到parent的左节点还是右节点if (kot(parent->_data) < kot(data))parent->_right = cur;elseparent->_left = cur;//链接父亲cur->_parent = parent;//父亲是红色节点,出现连续的红色节点,要处理while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//判断叔叔是grandfather的左节点还是右节点if (parent == grandfather->_left){Node* uncle = grandfather->_right;//uncle存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上更新颜色cur = grandfather;parent = cur->_parent;}else  //uncle不存在 或者 uncle存在且为黑{if (cur == parent->_left){//     g//   p    u// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   p    u//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else if (parent == grandfather->_right){Node* uncle = grandfather->_left;//uncle存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上更新颜色cur = grandfather;parent = cur->_parent;}else //uncle不存在 或者 uncle存在且为黑{if (cur == parent->_right){//     g//   u   p//        cRotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   u    p //      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}//更新颜色时,_root的颜色可能会改变//当grandfather是_root时//    g        更新颜色时,parent和uncle会变黑//  p   u      grandfather会变红// c//所以必须加这句代码保证_root的颜色为黑。_root->_col = BLACK;return { Iterator(newnode, _root), true };
}

红黑树的默认成员函数

主要是补充之前实现红黑树时没有写的拷贝构造函数、赋值重载函数和析构函数。

​//默认构造RBTree() = default;//拷贝构造RBTree(const RBTree<K, T, KeyOfT>& t){_root = Copy(t._root);}//赋值重载RBTree<K, T, KeyOfT>& operator=(const RBTree<K, T, KeyOfT> t){std::swap(_root, t._root);return *this;}//析构~RBTree(){Destroy(_root);_root = nullptr;}Node* Copy(Node* root){if (root == nullptr)return nullptr;//前序遍历复制Node* newnode = new Node(root->_data);newnode->_col = root->_col;newnode->_left = Copy(root->_left);if (root->_left)root->_left->_parent = newnode;newnode->_right = Copy(root->_right);if (root->_right)root->_right->_parent = newnode;return newnode;}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}​

修改后完整的红黑树 

​
enum Color
{RED,BLACK
};//T代表set传过来的key或者map传过来的(pair)key-value
template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;Node* _root;RBTreeIterator(Node* node, Node* root):_node(node),_root(root){}//前置++,返回++之后的值Self operator++(){// 当前节点的右子树不为空,中序的下一个节点是// 当前节点的右子树的最左(最小)节点if (_node->_right){Node* rightMin = _node->_right;while (rightMin->_left)rightMin = rightMin->_left;_node = rightMin;}//当前节点的右子树为空,说明当前节点所属的子树已经中序遍历访问完毕//往上寻找中序遍历的下一个节点,找到一个节点是父亲的左节点的特殊节点//此时特殊节点的父亲就是当前节点中序遍历的下一个节点else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//后置++Self operator++(int){Self tmp = *this;++(*this);return tmp;}//前置--Self operator--(){//处理end()的情况//--end()得到的应该是中序遍历的最后一个节点//整一棵树的最右节点if (_node == nullptr){Node* rightMost = _root;while (rightMost && rightMost->_right)rightMost = rightMost->_right;_node = rightMost;}//当前节点的左子树不为空,找左子树的最右节点else if (_node->_left){Node* leftMost = _node->_left;while (leftMost && leftMost->_right)leftMost = leftMost->_right;_node = leftMost;}//当前节点的左子树为空else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}//后置--Self operator--(int){Self tmp = *this;--(*this);return tmp;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}};template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef RBTreeIterator<T, T&, T*> Iterator;typedef RBTreeIterator<T, const T&, const T*> Const_Iterator;//默认构造RBTree() = default;//拷贝构造RBTree(const RBTree<K, T, KeyOfT>& t){_root = Copy(t._root);}//赋值重载RBTree<K, T, KeyOfT>& operator=(const RBTree<K, T, KeyOfT> t){std::swap(_root, t._root);return *this;}//析构~RBTree(){Destroy(_root);_root = nullptr;}//中序遍历的第一个节点是整棵树的最左节点Iterator begin(){Node* cur = _root;while (cur && cur->_left)cur = cur->_left;return Iterator(cur, _root);}Iterator end(){return Iterator(nullptr, _root);}Const_Iterator begin() const{Node* cur = _root;while (cur && cur->_left)cur = cur->_left;return Const_Iterator(cur, _root);}Const_Iterator end() const{return Const_Iterator(nullptr, _root);}void RotateR(Node* parent){//subL为parent的左孩子节点Node* subL = parent->_left;//subLR为subL的右子节点Node* subLR = subL->_right;// 将parent与subLR节点进行链接parent->_left = subLR;//在SubLR的情况下更改,让其指向正确的父亲if (subLR)subLR->_parent = parent;//提前记录祖父节点Node* pParent = parent->_parent;//链接subL与parentsubL->_right = parent;parent->_parent = subL;//根据parent是否是根节点进行不同处理if (parent == _root){_root = subL;subL->_parent = nullptr;}else{//将pParent和subL链接//但得先判断parent是pParent的左节点还是右节点if (pParent->_left == parent)pParent->_left = subL;elsepParent->_right = subL;//修改subL的parent指针,让其指向正确的父亲subL->_parent = pParent;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* pParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pParent->_left == parent)pParent->_left = subR;elsepParent->_right = subR;subR->_parent = pParent;}}pair<Iterator, bool> Insert(const T& data){//按二叉搜索树插入if (_root == nullptr){_root = new Node(data);//根节点为黑色_root->_col = BLACK;//return pair<Iterator, bool>(Iterator(_root, _root), true);return { Iterator(_root, _root), true };}//仿函数,获取T中的具体的keyKeyOfT 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;}elsereturn { Iterator(cur, _root), false };}cur = new Node(data);Node* newnode = cur;//非空树插入红色节点cur->_col = RED;//判断cur应该插入到parent的左节点还是右节点if (kot(parent->_data) < kot(data))parent->_right = cur;elseparent->_left = cur;//链接父亲cur->_parent = parent;//父亲是红色节点,出现连续的红色节点,要处理while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//判断叔叔是grandfather的左节点还是右节点if (parent == grandfather->_left){Node* uncle = grandfather->_right;//uncle存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上更新颜色cur = grandfather;parent = cur->_parent;}else  //uncle不存在 或者 uncle存在且为黑{if (cur == parent->_left){//     g//   p    u// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   p    u//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else if (parent == grandfather->_right){Node* uncle = grandfather->_left;//uncle存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上更新颜色cur = grandfather;parent = cur->_parent;}else //uncle不存在 或者 uncle存在且为黑{if (cur == parent->_right){//     g//   u   p//        cRotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   u    p //      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}//更新颜色时,_root的颜色可能会改变//当grandfather是_root时//    g        更新颜色时,parent和uncle会变黑//  p   u      grandfather会变红// c//所以必须加这句代码保证_root的颜色为黑。_root->_col = BLACK;return { Iterator(newnode, _root), true };}Iterator Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (key > kot(cur->_data))cur = cur->_right;else if (key < kot(cur->_data))cur = cur->_left;elsereturn Iterator(cur, _root);}return Iterator(nullptr, _root);}int Height(){return _Height(_root);}int Size(){return _Size(_root);}private:int _Height(Node* root){if (root == nullptr) return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}int _Size(Node* root){if (root == nullptr) return 0;return _Size(root->_left) + _Size(root->_right) + 1;}Node* Copy(Node* root){if (root == nullptr)return nullptr;//前序遍历复制Node* newnode = new Node(root->_data);newnode->_col = root->_col;newnode->_left = Copy(root->_left);if (root->_left)root->_left->_parent = newnode;newnode->_right = Copy(root->_right);if (root->_right)root->_right->_parent = newnode;}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}private:Node* _root = nullptr;
};​

set、map的模拟实现

对红黑树进行修改后,只需对其接口函数进行封装和实现KeyOfT仿函数即可。

set

template<class K>
class set
{//实现keystruct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const 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();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}iterator Find(const K& key){return _t.Find(key);}private://加const令其不能修改keyRBTree<K, const K, SetKeyOfT> _t;};

map

class map
{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};
public: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();}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = insert({ key, V() });return ret.first->second;}iterator Find(const K& key){return _t.Find(key);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

测试封装的set和map

void test_myset1()
{zh::set<int> s;s.insert(3);s.insert(1);s.insert(5);s.insert(4);s.insert(6);auto it = s.Find(3);cout << *it << endl;it++;cout << *it << endl;auto it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;
}

void test_Mymap1()
{zh::map<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "left", "左边" });dict.insert({ "right", "右边" });auto it = dict.Find("sort");cout << it->first << ":" << it->second << endl;cout << endl;it = dict.begin();while (it != dict.end()){//it->first += 'x';it->second += 'x';cout << it->first << ":" << it->second << endl;it++;}cout << endl;it = dict.end();it--;while (it != dict.begin()){cout << it->first << ":" << it->second << endl;it--;}cout << endl;dict["left"] = "左边,剩余";dict["insert"] = "插入";dict["string"];for (auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}
}


 拜拜,下期再见😏

摸鱼ing😴✨🎞

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

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

相关文章

Spring Boot框架:电商系统的技术革新

4 系统设计 网上商城系统的设计方案比如功能框架的设计&#xff0c;比如数据库的设计的好坏也就决定了该系统在开发层面是否高效&#xff0c;以及在系统维护层面是否容易维护和升级&#xff0c;因为在系统实现阶段是需要考虑用户的所有需求&#xff0c;要是在设计阶段没有经过全…

【Linux】介绍和基础01

Linux介绍 linux是一个操作系统 和 windows平级 虚拟机 运行Linux在当前系统的 ‘另一个电脑’ 虚拟机可以运行多个‘电脑’ 你在哪 决定操作文件 ~ 波浪线代表当前登录的家 root 用户没有单独的家 整个操作都是root的家 Ubuntu没有盘的概念 所有的linux中都是文件 文…

(一)Ubuntu20.04服务器端部署Stable-Diffusion-webui AI绘画环境

一、说明 cup型号&#xff1a; Intel(R) Celeron(R) CPU G1610 2.60GHz 内存大小&#xff1a; 7.5Gi 356Mi 4.6Gi 1.0Mi 2.6Gi 6.8Gi Swap: 4.0Gi 0B 4.0Gi 显卡型号&#xff1a;NVIDIA P104-100 注意&#xff1a…

IQ Offset之工厂实例分析

有个产品 其方块图如下: FEM全名为Front End Module 详情可参照这篇 [1] WIFI前端模块的解析 这边就不赘述 而在工厂大量生产时 有一块板子 其Chain1的EVM Fail 分析Log后 发现其IQ Offset的值 比Chain2/Chain3/Chain4 还要来得差 请问 问题是出在收发器? 还是…

c++ 类和对象(中)

前言 我们看看下面的代码以及代码运行结果 代码1 我们可以看到在我们的类Data中的函数成员print中&#xff0c;我们并没有设置形参&#xff0c;在调用此函数时&#xff0c;也并没有多余传参&#xff0c;但是我们调用它时&#xff0c;却能准确打印出我们的_year、_month、_day…

TCP(下):三次握手四次挥手 动态控制

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持! TCP(上)&#xff1a;成熟可靠的传输层协议-CSDN博客 &#x1f95d;在上篇博客中&#xff0c;我们针对TCP的特性,报文结构,连接过程以及相对于其他协议的区别进行了探讨&#xff0c;提供了初步的理解和概览。本…

后端——接口文档(API)

一、概念 后端的接口文档&#xff08;API文档&#xff09;——全称为应用程序编程接口&#xff08;Application Programming Interface&#xff09;文档&#xff0c;是详细阐述特定软件应用程序或Web服务所开放接口的具体使用指南。这份文档为开发者提供了与这些接口进行交互的…

【Linux:epoll】

目录 epoll与select、poll的区别&#xff1a; epoll操作函数&#xff1a; int epoll_create(int size); epoll_ctl: epoll_wait: epoll与select、poll的区别&#xff1a; select,poll底层是一个线性表的结构&#xff0c;而epoll是一个红黑树结构。epoll、poll不能跨平台…

Java基础——多线程

1. 线程 是一个程序内部的一条执行流程程序中如果只有一条执行流程&#xff0c;那这个程序就是单线程的程序 2. 多线程 指从软硬件上实现的多条执行流程的技术&#xff08;多条线程由CPU负责调度执行&#xff09; 2.1. 如何创建多条线程 Java通过java.lang.Thread类的对象…

使用Redis的一些经验总结

目录 一、Redis中的key和value的设计 1.key的命名规范 2.避免出现BigKey 3.value中选择恰当的数据类型 例1&#xff1a;比如存储一个User对象&#xff0c;我们有三种存储方式&#xff1a; ①方式一&#xff1a;json字符串 ②方式二&#xff1a;字段打散 ③方式三&#x…

如何在 Ubuntu 上配置 Kotlin 应用环境 ?

Kotlin 是一种运行在 Java 虚拟机 (JVM) 上的现代编程语言&#xff0c;它同时支持函数式和面向对象编程。它可与 Java 互操作&#xff0c;并以其简洁的语法而闻名。在本指南中&#xff0c;我们将介绍在 Ubuntu 系统上准备好 Kotlin 开发环境的要点&#xff0c;包括 Java、Kotli…

2024强化学习的结构化剪枝模型RL-Pruner原理及实践

[2024] RL-Pruner: Structured Pruning Using Reinforcement Learning for CNN Compression and Acceleration 目录 [2024] RL-Pruner: Structured Pruning Using Reinforcement Learning for CNN Compression and Acceleration一、论文说明二、原理三、实验与分析1、环境配置在…

嵌入式硬件电子电路设计(五)MOS管详解(NMOS、PMOS、三极管跟mos管的区别)

引言&#xff1a;在我们的日常使用中&#xff0c;MOS就是个纯粹的电子开关&#xff0c;虽然MOS管也有放大作用&#xff0c;但是几乎用不到&#xff0c;只用它的开关作用&#xff0c;一般的电机驱动&#xff0c;开关电源&#xff0c;逆变器等大功率设备&#xff0c;全部使用MOS管…

蓝桥杯每日真题 - 第14天

题目&#xff1a;&#xff08;2022&#xff09; 题目描述&#xff08;13届 C&C B组A题&#xff09; 解题思路&#xff1a; 定义状态&#xff1a; 使用一个二维数组 dp[j][k] 来表示将数字 k 拆分为 j 个不同正整数的方案数。 初始化&#xff1a; 初始状态设定为 dp[0][0]…

利用云计算实现高效的数据备份与恢复策略

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 利用云计算实现高效的数据备份与恢复策略 利用云计算实现高效的数据备份与恢复策略 利用云计算实现高效的数据备份与恢复策略 引…

thinkphp6配置多应用项目及多域名访问路由app配置

这里写一写TP6下配置多应用。TP6默认是单应用模式&#xff08;单模块&#xff09;&#xff0c;而我们实际项目中往往是多应用的&#xff08;多个模块&#xff09;&#xff0c;所以在利用TP6是就需要进行配置&#xff0c;开启多应用模式。 1、安装ThinkPHP6 1.1安装ThinkPHP6.…

JavaScript:浏览器对象模型BOM

BOM介绍 浏览器对象模型&#xff08;Brower Object Model&#xff0c;BOM&#xff09;提供了独立于内容而与浏览器窗口进行交互的对象&#xff0c;其核心对象是window BOM由一系列相关的对象构成&#xff0c;并且每个对象都提供了很多方法和属性。 BOM与DOM区别 DOM是文档对…

SpringBoot 2.2.10 无法执行Test单元测试

很早之前的项目今天clone现在&#xff0c;想执行一个业务订单的检查&#xff0c;该检查的代码放在test单元测试中&#xff0c;启动也是好好的&#xff0c;当点击对应的方法执行Test的时候就报错 tip&#xff1a;已添加spring-boot-test-starter 所以本身就引入了junit5的库 No…

前后端、网关、协议方面补充

这里写目录标题 前后端接口文档简介前后端视角对于前端对于后端代码注册路由路由处理函数 关于httpGET/POST底层网络关于前端的获取 路由器网关路由器的IP简介公网IP(WAN IP)私网IP(LAN IP)无线网络IP(WIFI IP)查询路由器私网IP路由器公网IP LAN口与WIFI简介基本原理 手动配置电…

英伟达基于Mistral 7B开发新一代Embedding模型——NV-Embed-v2

我们介绍的 NV-Embed-v2 是一种通用嵌入模型&#xff0c;它在大规模文本嵌入基准&#xff08;MTEB 基准&#xff09;&#xff08;截至 2024 年 8 月 30 日&#xff09;的 56 项文本嵌入任务中以 72.31 的高分排名第一。此外&#xff0c;它还在检索子类别中排名第一&#xff08;…