49 set与map的模拟实现
目录
一、源码及框架分析
二、模拟实现map和set
(一)复用红黑树的框架,并支持insert
(二)支持迭代器的实现
(三)map支持 [ ]
(四)整体代码实现
一、源码及框架分析
SGI-STL30版本源代码,map和set的源代码在map/set/stl_map.h/stl_set.h/stl_tree.h等几个头文件中。
map和set的实现结构框架核心部分截取出来如下:
#define _CRT_SECURE_NO_WARNINGS 1// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:// typedefs:typedef Key key_type;typedef Key value_type;
private:typedef rb_tree<key_type, value_type,identity<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing set
};// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:// typedefs:typedef Key key_type;typedef T mapped_type;typedef pair<const Key, T> value_type;
private:typedef rb_tree<key_type, value_type,select1st<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing map
};// stl_tree.h 源码这里把颜色的定义与变量的定义分开
struct __rb_tree_node_base
{typedef __rb_tree_color_type color_type;typedef __rb_tree_node_base* base_ptr;color_type color;base_ptr parent;base_ptr left;base_ptr right;
};// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc= alloc>
class rb_tree {
protected:typedef void* void_pointer;typedef __rb_tree_node_base* base_ptr;typedef __rb_tree_node<Value> rb_tree_node;typedef rb_tree_node* link_type;typedef Key key_type;typedef Value value_type;
public:// insert用的是第二个模板参数左形参pair<iterator, bool> insert_unique(const value_type& x);// erase和find⽤第⼀个模板参数做形参size_type erase(const key_type& x);iterator find(const key_type& x);
protected:size_type node_count; // keeps track of size of treelink_type header;
};template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{typedef __rb_tree_node<Value>* link_type;Value value_field;
};
• 通过下图对框架的分析,可以看到源码中rb_tree用了一个巧妙的泛型思想实现,rb_tree是实现key的搜索场景,还是key/value的搜索场景不是直接写死的,而是由第二个模板参数Value决定_rb_tree_node中存储的数据类型。
• set实例化rb_tree时第二个模板参数给的是key,map实例化rb_tree时第二个模板参数给的是 pair<const key, T>,这样一颗红黑树既可以实现key搜索场景的set,也可以实现key/value搜索场 景的map。
• 要注意一下,源码里面模板参数是用T代表value,而内部写的value_type不是我们我们日常 key/value场景中说的value,源码中的value_type反而是红黑树结点中存储的真实的数据的类型。
• rb_tree第二个模板参数Value已经控制了红黑树结点中存储的数据类型,为什么还要传第一个模板参数Key呢?尤其是set,两个模板参数是一样的。要注意的是对于map和set,find/erase时的函数参数都是Key,所以第一个模板参数是传给find/erase等函数做形参的类型的。对于set而言两个参数是一样的(与map进行兼容),但是对于map而言就完全不一样了,map insert的是pair对象,但是find和ease的是Key对象。
• 注意,这里源码命名风格比较乱,set模板参数用的Key命名,map用的是Key和T命名,而rb_tree用的又是Key和Value。
二、模拟实现map和set
(一)复用红黑树的框架,并支持insert
• 参考源码框架,map和set复用之前我们实现的红黑树。
• 我们这里相比源码调整一下,key参数就用K,value参数就用V,红黑树中的数据类型,我们使用T。
• 其次因为若RBTree实现的泛型中不知道T参数是K,还是pair<K, V>,那么insert内部进行插入逻辑比较时,就没办法进行比较,因为pair的默认支持的是key和value一起参与比较,我们需要在任何时候只比较key,所以我们在map和set层分别实现⼀个MapKeyOfT和SetKeyOfT的仿函数传给RBTree的KeyOfT,然后RBTree中通过KeyOfT仿函数取出T类型对象中的key,再进行比较,具体细节参考如下代码实现。
// 源码中pair⽀持的<重载实现
template <class T1, class T2>
bool operator< (const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{return lhs.first < rhs.first || (!(rhs.first < lhs.first) &&lhs.second < rhs.second);
}
//pair原来的比较是先比较first,first一样的话就比较second;而我们需要的是只对first进行比较。
Mymap.h
namespace zyb {template<class K, class V>class map{struct MapKeyOfT//返回first{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:bool insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<K, V>, MapKeyOfT> _t;}; }
Myset.h
namespace zyb {template<class K>class set{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;}; }
RBTree.h
enum Colour {RED,BLACK }; template<class T> struct RBTreeNode {T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& data): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr){} };// 实现步骤: // 1、实现红黑树 // 2、封装map和set框架,解决KeyOfT // 3、iterator // 4、const_iterator // 5、key不支持修改的问题 // 6、operator[]template<class K, class T, class KeyOfT> class RBTree { private:typedef RBTreeNode<T> Node;Node* _root = nullptr; public: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会随着颜色的更新而移动,需要记录一下// 新增结点。颜色给红色cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//...return true;} }
(二)支持迭代器的实现
iterator核心源代码:
struct __rb_tree_base_iterator
{typedef __rb_tree_node_base::base_ptr base_ptr;base_ptr node;void increment(){if (node->right != 0) {node = node->right;while (node->left != 0)node = node->left;}else {base_ptr y = node->parent;while (node == y->right) {node = y;y = y->parent;}if (node->right != y)node = y;}}void decrement(){if (node->color == __rb_tree_red &&node->parent->parent == node)node = node->right;else if (node->left != 0) {base_ptr y = node->left;while (y->right != 0)y = y->right;node = y;}else {base_ptr y = node->parent;while (node == y->left) {node = y;y = y->parent;}node = y;}}
};template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{typedef Value value_type;typedef Ref reference;typedef Ptr pointer;typedef __rb_tree_iterator<Value, Value&, Value*> iterator;__rb_tree_iterator() {}__rb_tree_iterator(link_type x) { node = x; }__rb_tree_iterator(const iterator& it) { node = it.node; }reference operator*() const { return link_type(node)->value_field; }
#ifndef __SGI_STL_NO_ARROW_OPERATORpointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */self& operator++() { increment(); return *this; }self& operator--() { decrement(); return *this; }inline bool operator==(const __rb_tree_base_iterator& x,const __rb_tree_base_iterator& y) {return x.node == y.node;}inline bool operator!=(const __rb_tree_base_iterator& x,const __rb_tree_base_iterator& y) {return x.node != y.node;
}
iterator实现思路分析:
• iterator实现的大框架跟list的iterator思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为。
• 这里的难点是operator++和operator--的实现。之前使用部分,我们分析了,map和set的迭代器走的是中序遍历,左子树->根节点->右子树,那么begin()会返回中序第一个结点的iterator也就是10所在结点的迭代器。
• 迭代器++的核心逻辑就是不看全局,只看局部,只考虑当前中序局部要访问的下⼀个结点。
• 迭代器++时,如果it指向的结点的右子树不为空,代表当前结点已经访问完了,要访问下一个结点是右子树的中序第一个,一棵树中序第一个是最左结点,所以直接找右子树的最左结点即可。
• 迭代器++时,如果it指向的结点的右子树为空,代表当前结点已经访问完了且当前结点所在的子树也访问完了,要访问的下一个结点在当前结点的祖先节点里面,所以要沿着当前结点到根的祖先路径向上找。
• 如果当前结点是父亲的左,根据中序左子树->根结点->右子树,那么下一个访问的结点就是当前结点的父亲;如下图:it指向25,25右为空,25是30的左,所以下⼀个访问的结点就是30。
• 如果当前结点是父亲的右,根据中序左子树->根结点->右子树,当前结点所在的子树访问完了,当前结点所在的父亲节点的子树也访问完了,那么下⼀个访问的需要继续往根的祖先中去找,直到找到【孩子是父亲左】的那个祖先就是中序要问题的下⼀个结点。如下图:it指向15,15右为空,15是10的右,15所在子树话访问完了,10所在子树也访问完了,继续往上找,10是18的左,那么下⼀个访问的结点就是18。
• end()如何表示呢?如下图:当it指向50时,++it时,50是40的右,40是30的右,30是18的右,18到根没有父亲,没有找到孩子是父亲左的那个祖先,这是因为父亲为空了,那我们就把it中的结点指针置为nullptr,我们用nullptr去充当end。需要注意的是stl源码中,红黑树增加了⼀个哨兵位头结点做为end(),这哨兵位头结点和根互为父亲,左指向最左结点,右指向最右结点。相比我们用nullptr作为end(),差别不大,他能实现的,我们也能实现。只是--end()判断到结点时空,特殊处理⼀下,让迭代器结点指向最右结点。具体参考迭代器--实现。
• 迭代器--的实现跟++的思路完全类似,逻辑正好反过来即可,因为他访问顺序是右子树->根结点->左子树,具体参考下面代码实现。
• set的iterator也不支持修改,我们把set的第⼆个模板参数改成const K即可:
RBTree<K, const K, SetKeyOfT> _t;
• map的iterator不支持修改key但是可以修改value,我们把map的第二个模板参数pair的第一个参数改成const K即可,即:RBTree<K, pair<const K, V>, MapKeyOfT> _t;
• 支持完整的迭代器还有很多细节需要修改,具体参考下面题的代码。
(三)map支持 [ ]
• map要支持[]主要需要修改insert返回值支持,修改RBtree中的insert返回值为:
pair<Iterator, bool> Insert(const T& data)
• 有了insert支持[]实现就很简单了,具体参考下面代码实现。
(四)整体代码实现
Myset.h
#include"RBTree.h" namespace zyb {template<class K>class set{struct 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>::ConstIteratorconst_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:RBTree<K, const K, SetKeyOfT> _t;};void Print(const set<int>& s){set<int>::const_iterator it = s.end();while (it != s.begin()){--it;// 不⽀持修改//*it += 2;cout << *it << " ";}cout << endl;}//测试代码void test_set(){set<int> s;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){s.insert(e);}for (auto e : s){cout << e << " ";}cout << endl;Print(s);} }
Mymap.h#include"RBTree.h" namespace zyb {template<class K, class V>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>::Iteratoriterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIteratorconst_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);}iterator find(const K& key){return _t.Find(key);}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};//测试代码void test_map(){map<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "left", "左边" });dict.insert({ "right", "右边" });dict["left"] = "左边,剩余";dict["insert"] = "插⼊";dict["string"];map<string, string>::iterator it = dict.begin();while (it != dict.end()){// 不能修改first,可以修改second//it->first += 'x';it->second += 'x';cout << it->first << ":" << it->second << endl;++it;}cout << endl;} }
RBtree.henum Colour {RED,BLACK }; template<class T> struct RBTreeNode {T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _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* leftMost = _node->_right;while (leftMost->_left){leftMost = leftMost->_left;}_node = leftMost;}else{// 孩⼦是父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node == nullptr) // end(){// --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else if (_node->_left){// 左子树不为空,中序左子树最后一个Node* rightMost = _node->_left;while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else{// 孩子是父亲右的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}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*> ConstIterator;Iterator Begin(){Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return Iterator(leftMost, _root);}Iterator End(){return Iterator(nullptr, _root);}ConstIterator Begin() const{Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return ConstIterator(leftMost, _root);}ConstIterator End() const{return ConstIterator(nullptr, _root);}RBTree() = default;~RBTree(){Destroy(_root);_root = nullptr;}pair<Iterator, bool> Insert(const T & data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(Iterator(_root, _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, _root), false);}}cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else {if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode, _root), true); } Iterator Find(const K& key) {Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return Iterator(cur, _root);}}return End(); } private:void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;} private:Node* _root = nullptr; };
以上内容仅供分享,若有错误,请多指正。
相关文章:
49 set与map的模拟实现
目录 一、源码及框架分析 二、模拟实现map和set (一)复用红黑树的框架,并支持insert (二)支持迭代器的实现 (三)map支持 [ ] (四)整体代码实现 一、源码及框架分析…...
鸿蒙NEXT应用App测试-通用测试
注意:大家记得学完通用测试记得再学鸿蒙专项测试 https://blog.csdn.net/weixin_51166786/article/details/145768653 注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下 如果大家觉得博主文章…...
LangChain 技术入门指南:探索语言模型的无限可能
在当今的技术领域,LangChain 正逐渐崭露头角,成为开发语言模型应用的强大工具。如果你渴望深入了解并掌握这一技术,那么就跟随本文一起开启 LangChain 的入门之旅吧! (后续将持续输出关于LangChain的技术文章,有兴趣的同学可以关注…...
UE5销毁Actor,移动Actor,简单的空气墙的制作
1.销毁Actor 1.Actor中存在Destory()函数和Destoryed()函数 Destory()函数是成员函数,它会立即标记 Actor 为销毁状态,并且会从场景中移除该 Actor。它会触发生命周期中的销毁过程,调用 Destroy() 后,Actor 立即进入销毁过程。具体…...
STM32基础篇(二)------GPIO
GPIO简介 GPIO(General Purpose Input Output)通用输入输出口 可配置为8种输入输出模式 引脚电平:0V~3.3V,部分引脚可容忍5V 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等 输入…...
亲测Win11电脑可以安装LabVIEW的版本,及2015、2018、2020版本直接的区别
下面是我电脑的信息 设备名称 DESKTOP-04HHS8S 处理器 13th Gen Intel(R) Core(TM) i5-13500H 2.60 GHz 机带 RAM 16.0 GB (15.7 GB 可用) 设备 ID 82798104-C565-4167-A21E-5EB5DEFAA541 产品 ID 00331-20300-00000-AA678 系统类型 64 位操作系统, 基于 …...
Idea2024中搭建JavaFX开发环境并创建运行项目
Idea2024中搭建JavaFX开发环境并创建运行项目 本文以Java语言为例演示如何创建JavaFX开发项目和部署开发环境,读者可以根据个人实际灵活选择相关参数。 一、项目创建与环境搭建步骤 新建JavaFX项目,选择适合项目实际的语言、系统和JDK。 项目设置-设置…...
认知重构 | 自我分化 | 苏格拉底式提问
注:本文为 “认知重构 | 自我分化” 相关文章合辑。 心理学上有一个词叫:认知重构(改变 “非黑即白,一分为二” 的思维方式) 原创 心理师威叔 心理自救 2024 年 10 月 26 日 19:08 广东 你有没有过这样的时候&#x…...
MFC开发:如何创建第一个MFC应用程序
文章目录 一、概述二、MFC 的主要组件三、创建一个MFC窗口四、控件绑定消息函数 一、概述 MFC 是微软提供的一个 C 类库,用于简化 Windows 应用程序的开发。它封装了 Windows API,提供面向对象的接口,帮助开发者更高效地创建图形用户界面&am…...
nodejs:vue 3 + vite 作为前端,将 html 填入<iframe>,在线查询英汉词典
向 doubao.com/chat/ 提问: node.js js-mdict 作为后端,vue 3 vite 作为前端,编写在线查询英汉词典 后端部分(express js-mdict ) 详见上一篇:nodejs:express js-mdict 作为后端ÿ…...
基于 Python Django 的校园互助平台(附源码,文档)
博主介绍:✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇🏻 不…...
玩转 Java 与 Python 交互,JEP 库来助力
文章目录 玩转 Java 与 Python 交互,JEP 库来助力一、背景介绍二、JEP 库是什么?三、如何安装 JEP 库?四、JEP 库的简单使用方法五、JEP 库的实际应用场景场景 1:数据处理场景 2:机器学习场景 3:科学计算场…...
【Linux】基于UDP/TCP服务器与客户端的实现
目录 一、UDP (一)Server.hpp (二)Server.cpp (三)Client.hpp (四)Client.cpp (五)User.hpp 二、TCP (一)多进程版本的服务器与…...
Unity for Python —— 强大的 Python 脚本支持提升 Unity 编辑器效率
内容将会持续更新,有错误的地方欢迎指正,谢谢! Unity for Python —— 强大的 Python 脚本支持提升 Unity 编辑器效率 TechX 坚持将创新的科技带给世界! 拥有更好的学习体验 —— 不断努力,不断进步,不断探索 Tec…...
【Dubbo+Zookeeper】——SpringBoot+Dubbo+Zookeeper知识整合
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大二学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
家用路由器的WAN口和LAN口有什么区别
今时今日,移动终端盛行的时代,WIFI可以说是家家户户都有使用到的网络接入方式。那么路由器当然也就是家家户户都不可或缺的设备了。而路由器上的两个实现网络连接的基础接口 ——WAN 口和 LAN 口,到底有什么区别?它们的功能和作用…...
Python--函数入门
1. 函数基础概念 1.1 什么是函数 定义:函数是一段可重复调用的代码块,用于封装特定功能。 核心作用: 代码重用:减少重复代码的编写。增强可读性:通过命名和模块化让代码逻辑更清晰。 1.2 函数的定义与调用 def 函…...
EasyRTC低延迟通信与智能处理:论嵌入式WebRTC与AI大模型的技术融合
在当今数字化时代,实时通信的需求日益增长,视频通话作为一种高效、直观的沟通方式,广泛应用于各个领域。WebRTC技术的出现,为实现浏览器之间的实时音视频通信提供了便捷的解决方案。而基于WebRTC技术的EasyRTC视频通话SDK…...
《操作系统 - 清华大学》 8 -6:进程管理:进程状态变化模型
进程状态及其转换全解析 在操作系统中,进程有着特定的生命周期和多种状态变化。不考虑进程结束时,进程主要有三个基本状态。 运行态:即进程正在占用CPU执行任务。总结:运行态表示进程当前正在使用CPU。就绪状态:进程…...
大语言模型中的 Token如何理解?
在大语言模型中,Token 是文本处理的基本单元,类似于“文字块”,模型通过将文本分割成Token来理解和生成内容。举一个形象一点的例子,可以理解为 AI 处理文字时的“最小积木块”。就像搭乐高时,每块积木是基础单位一样&…...
信息学奥赛一本通 1522:网络 | OpenJudge 百练 1144:Network
【题目链接】 ybt 1522:网络 OpenJudge 百练 1144:Network 【题目考点】 1. 图论:割点 【解题思路】 每个交换机是一个顶点,如果两地点之间有电话线连接,那么两顶点之间有一条无向边,该图是无向图。 初始时任何地…...
3分钟快速本地部署deepseek
DeepSeek简介 DeepSeek 是杭州深度求索人工智能基础技术研究有限公司开发的一系列大语言模型,背后是知名量化资管巨头幻方量化3。它专注于开发先进的大语言模型和相关技术,拥有多个版本的模型,如 DeepSeek-LLM、DeepSeek-V2、DeepSeek-V3 等…...
Linux系统管理与编程01:准备工作
0 准备工作 0.1 安装VMWare Workstation pro17 到百度搜一下,到处都是。安装好VMWare Workstation pro17(以下简称VW)。 图0- 1 安装过程略。 0.2下载CentOS7.6 图0- 2 选择minimal版本。 0.3下载yum库文件 下载阿里云yum库文件https:…...
常用的几种编码方式
常见的编码方式有多种,每种编码方式都有其特定的用途和特点。以下是几种常见的编码方式: ASCII(美国信息交换标准代码) 用途:主要用于表示英文字符及控制字符。特点:使用7位二进制数表示字符,能…...
WebXR教学 03 项目1 旋转彩色方块
一、项目结构 webgl-cube/ ├── index.html ├── main.js ├── package.json └── vite.config.js二、详细实现步骤 初始化项目 npm init -y npm install three vite --save-devindex.html <!DOCTYPE html> <html lang"en"> <head><…...
从零开始的网站搭建(以照片/文本/视频信息通信网站为例)
本文面向已经有一些编程基础(会至少一门编程语言,比如python),但是没有搭建过web应用的人群,会写得尽量细致。重点介绍流程和部署云端的步骤,具体javascript代码怎么写之类的,这里不会涉及。 搭…...
netcore 启用gzip压缩及缓存
public void ConfigureServices(IServiceCollection services) {....// 配置gzip 与 br的压缩等级为最优services.Configure<BrotliCompressionProviderOptions>(options > {options.Level CompressionLevel.Optimal;});services.Configure<GzipCompressionProvid…...
c++入门-------命名空间、缺省参数、函数重载
C系列 文章目录 C系列前言一、命名空间二、缺省参数2.1、缺省参数概念2.2、 缺省参数分类2.2.1、全缺省参数2.2.2、半缺省参数 2.3、缺省参数的特点 三、函数重载3.1、函数重载概念3.2、构成函数重载的条件3.2.1、参数类型不同3.2.2、参数个数不同3.2.3、参数类型顺序不同 前言…...
elf_loader:一个使用Rust编写的ELF加载器
本文介绍一个使用Rust实现的ELF加载器。 下面是elf_loader的仓库链接: github: https://github.com/weizhiao/elf_loaderhttps://github.com/weizhiao/elf_loader crates.io: https://crates.io/crates/elf_loaderhttps://crates.io/cra…...
postman调用ollama的api
按照如下设置,不需要设置key 保持长会话的方法 # 首次请求 curl http://localhost:11434/api/generate -d {"model": "deepseek-r1:32b","prompt": "请永久记住:110,1-12,之后所有数学计算必…...
鸿蒙5.0实战案例:基于ArkUI的验证码实现
往期推文全新看点(文中附带全新鸿蒙5.0全栈学习笔录) ✏️ 鸿蒙(HarmonyOS)北向开发知识点记录~ ✏️ 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~ ✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景&#…...
通俗理解什么是云原生?
by deepseek。 一、核心理念:云原生到底是什么? 1. 一句话定义 云原生(Cloud Native) 是一种构建和运行应用程序的方法论,它利用云计算的优势(弹性、分布式、自动化),让软件从设计…...
基于PSO粒子群优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a/matlab2024b 3.部分核心程序 (完整版代码包含详细中文注释和操作步骤视频…...
什么是完全前向保密(PFS)?
在当今数字化时代,信息安全至关重要。而密码学中的完全前向保密(Perfect Forward Secrecy,简称PFS)技术,已经成为保障信息安全的关键一环。如果没有完全前向保密,一旦长期密钥被泄露,攻击者就可…...
Oracle备库srvctl start丢失某个原有的service_names的案例
最近在测试主备环境中使用srvctl添加新的service之后,srvctl start发现其中一个原本用于主备同步的service丢失了。 原始的参数文件中的service_names参数值如下(数据库中service_names的值也一样,省略查看步骤): [oraclesmartdbstb01 202502…...
重学SpringBoot3-怎样优雅停机
更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞??收藏评论 重学SpringBoot3-怎样优雅停机 1. 什么是优雅停机?2. Spring Boot 3 优雅停机的配置3. Tomcat 和 Reactor Netty 的优雅停机机制 3.1 Tomcat 优雅停机3.2 Reactor Netty 优…...
SkyWalking集成Kafka实现日志异步采集经验总结
SkyWalking日志异步采集架构 【重点知识】 1、【Agent】kafka-reporter-plugin-x.x.x.jar包放plugins目录后必走kafka(kafka没有正确配置就会报错) 2、【Agent】异步如不开启数据压缩,日志数据较大,pod多、业务大时容易造成网络…...
图论 之 BFS
文章目录 3243.新增道路查询后的最短距离1311.获取你好友已观看的视频 BFS:广度优先搜索(BFS) 是一种常用的算法,通常用于解决图或树的遍历问题,尤其是寻找最短路径或层级遍历的场景。BFS 的核心思想是使用队列(FIFO 数…...
rust学习笔记5-所有权机制
rust核心就是所有权机制,是其内存管理的核心特性,旨在消除内存安全问题(如空指针、悬垂指针、内存泄漏等)而无需依赖垃圾回收(GC) 1.首先看一下语义模型 当声明一个变量 let a "32";它的语义模…...
网站快速收录:如何优化网站404页面?
优化网站404页面是提升用户体验和SEO效果的重要一环。以下是一些优化404页面的建议: 一、设计友好的404页面 简洁明了的提示信息:使用清晰的语言告诉用户该页面不存在或已被删除,避免使用过于技术化的术语。 提供导航链接:在40…...
关于order by的sql注入实验
实验描述 本实验基于sqli-lab的第46关进行测试 本关的sql 语句为$sql "SELECT * FROM users ORDER BY $id" 利用sort进行sql注入,我们可以利用报错注入,延时注入来爆出数据 1.报错注入 1.手工测试 爆出数据库 ?sort(extractvalue(1, c…...
Docker(Nginx)部署Vue
简介:目标使用docker将vue生成的dist文件,结合nginx生成镜像,然后运行; 1、首选确保vue项目正确运行,并能正确打包dist文件; 2、查看已经生成的dist文件 3、将dist文件打包为rar文件或者zip文件…...
从函数到神经网络
一、从函数到神经网络 所有一切的前提是,你要相信这个世界上的所有逻辑和知识,都可以用一个函数来表示。Functions describe the world ! 比如输入物体的质量和加速度,根据牛顿第二定律,就可以得到物体施加的力,这就是…...
Python 字符串格式化 print
Python 字符串格式化 print flyfish 1. 使用百分号(%)操作符进行字符串格式化 百分号(%)操作符是 Python 中比较传统的字符串格式化方式,它的使用方式类似于 C 语言中的 printf 函数。 # 格式化整数 num 10 print…...
LabVIEW 中的 Bluetooth.llb 库
Bluetooth.llb 库位于C:\Program Files (x86)\National Instruments\LabVIEW 2019\vi.lib\Platform目录,它是 LabVIEW 平台下用于蓝牙通信相关操作的重要库。该库为 LabVIEW 开发者提供了一系列工具,用于实现设备间的蓝牙连接、数据传输与交互等功能&…...
MySQL | MySQL库、表的基本操作01
MySQL库、表的基本操作01 一、库操作1.1 查看数据库1.2 创建数据库1.3 选择数据库1.4 查看创建数据库的SQL语句1.5 修改数据库1.6 删除数据库 二、表操作2.1 创建数据表2.2 查看表2.3 查看表结构2.4 查看创建数据库的SQL语句2.5 修改表2.6 删除表 ⚠️MySQL版本 8.0 一、库操作…...
抖音试水AI分身;腾讯 AI 战略调整架构;百度旗下小度官宣接入DeepSeek...|网易数智日报
抖音试水AI分身,字节旗下AI智能体平台扣子已与抖音打通,相关功能内测中 2月19日消息,钛媒体App独家获悉,字节旗下AI智能体开发平台扣子(Coze)已与抖音打通,抖音创作者可在扣子智能体平台打造AI分…...
RPC 框架项目剖析
RPC 框架项目剖析 说明 本文用于梳理一个 rpc项目的实现细节,此项目基于cpp语言 大概三千行左右,用于学习目的。 项目链接:rpc项目 项目底层类 1.抽象消息类 描述: 各种消息的基类 属性: 消息id,消息类型…...
前端 fetch API 调用 Tushare 的数据接口获取免费的基金股票信息数据
要在前端使用 JavaScript 的 fetch API 调用 Tushare 的数据接口,您需要遵循以下步骤: 1. 注册 Tushare 账号并获取 Token 首先,访问 Tushare 官网 注册账号。注册成功后,登录账号,在个人中心获取您的 API Token。 …...
【SpringMVC】十分钟跑起来一个SpringMVC项目
目录标题 1 项目概述1.项目结构解析2. MVC项目的结构和每个组件的作用:3. 项目的工作流程:4 后期可以扩展的点:2.源码学习1. HelloController 类,Spring MVC控制器2 springmvc-servlet.xml - Spring MVC的主要配置文件3.web 目录 …...