【C++】:map和set的封装

朋友们、伙计们,我们又见面了,本期来给大家解读一下set和map的封装,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

1. stl库中的封装

2. 模拟实现的红黑树改进

2.1 存储数据的类型

2.2 添加提取类型的仿函数

2.2.1 改进红黑树的插入

3. 封装红黑树的迭代器

3.1 operator++逻辑

代码实现:

3.2 operator--逻辑

3.3 其他接口

4. 添加红黑树的迭代器

代码实现:

5. set的封装

5.1 set的插入 

set封装代码:

6. map的封装 

6.1 operator[]

map封装代码:

7. 添加迭代器之后的红黑树完整代码


1. stl库中的封装

set和map底层封装采用了红黑树,如果不了解红黑树的铁铁可以去:【C++】:红黑树

那么我们可以简单的来看一看库中对set和map的封装:

从库中可以看到,在传参的时候红黑树中的第二个模版参数决定的是红黑树中存的是什么类型的数据,这样就可以通过传参调用使set与map用一个红黑树即可,根据库中的红黑树,为了实现set与map的封装,首先对我们自己实现的红黑树做个简单的改进。 

2. 模拟实现的红黑树改进

模拟实现红黑树代码:https://blog.csdn.net/Yikefore/article/details/134885925?spm=1001.2014.3001.5501 

2.1 存储数据的类型

因为要根据第二个模版参数来构造红黑树的节点,所以需要将红黑树的节点结构做一变化:

只需要用一个模版参数来构造即可:

#pragma once//枚举定义节点颜色
enum Color 
{RED,    //红色BLACK   //黑色
};//红黑树节点的定义
template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;    //左子树节点RBTreeNode<T>* _right;   //右子树节点RBTreeNode<T>* _parent;  //父节点Color _col;              //节点颜色T _data;                 //节点的数据//节点的构造RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED),_data(data){}
};

2.2 添加提取类型的仿函数

由于红黑树种存储的数据类型是通过传参来决定的,由于红黑树的插入是需要通过比较大小的,那么在接下来的插入逻辑中我们并不知道是key模型还是key_value模型。

① 假设是key模型 -> 那么只比较这一个key的大小即可。

② 假设是key_value模型 -> 那么是需要比较key与key的大小,不需要关心value的大小。

如果我们使用的是map,那么就会传递一个pair,我们不妨来看一下pair默认的比较逻辑:

first和second有一个小就表示小,因此pair的默认比较逻辑是不符合我们的要求的,所以我们需要自己使用仿函数来获取key来进行比较。

    template<class K>class set{public://提取set的keystruct SetKeyOfT{const K& operator()(const K& key){return key;}};private:RBTree<K, K, SetKeyOfT> _tree;};template<class K, class V>class map{public://提取map的keystruct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};private:RBTree<K, V, MapKeyOfT> _tree;};

为了让set与key都可以使用同一个红黑树,所以也需要对set的key做以提取,这里就可以保证在红黑树的插入结构中的比较逻辑都用的key进行比较。

2.2.1 改进红黑树的插入

由于我们是根据第二个模板参数来确认红黑树节点的类型,所以再插入比较时需要用提取key的仿函数进行提取,再进行比较:

//插入bool Insert(const T& data){//为空可以直接插入,并将根节点置为黑色if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;KeyOfT kot;//不为空找到合适的插入位置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 false;}//链接//新插入的节点默认为红色节点cur = new Node(data);if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}//判断节点的颜色,是否破坏了平衡//......}

3. 封装红黑树的迭代器

红黑树的迭代器封装是一种类似于链表迭代器的样式,不熟悉的铁铁可以去看看:【STL】:list模拟实现

我们使用一份代码,通过传递参数控制const迭代器和非const迭代器。

3.1 operator++逻辑

红黑树的遍历是一种二叉树的中序遍历: 左子树 根 右子树

因此红黑树遍历出的结果是一个升序。我们以下面这棵红黑树为例来演示一下:

中序遍历的第一个节点是1, 最后一个节点是27

中序遍历结果是 1 6 8 11 13 15 17 22 25 27

中序遍历的第一个节点便是这棵红黑树的最左节点;

++it的核心:找中序遍历的下一个节点;

有两种情况:

1. it指向的当前节点,如果右子树不为空,那么下一个节点就是该右子树的最左节点。

2. it指向的当前节点,如果右子树为空,那么分两种情况:

① it指向的当前节点是父节点的左,那么就表明父节点的左子树已经访问完了,接下来直接访问父节点即可。

② it指向的当前结点是父节点的右,那么就表明以父节点为根的这棵子树全部访问完毕,需要访问下一棵子树,那么就要往上找孩子是父亲左的那个祖先节点。

代码实现:
// 红黑树迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef __TreeIterator<T, Ref, Ptr> Self;typedef RBTreeNode<T> Node;Node* _node;__TreeIterator(Node* node):_node(node){}Self& operator++(){if (_node->_right)  //右子树不为空,下一个节点就是右子树的最左节点{Node* cur = _node->_right;while (cur->left){cur = cur->_left;}_node = cur;}else               //右子树为空,下一个节点就是孩子为父亲左的那个祖先节点{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;	}return *this;}
};

3.2 operator--逻辑

operator--的逻辑和operator++逻辑正好相反: 右子树 根 左子树

1. it指向的当前节点,如果左子树不为空,那么下一个节点就是该左子树的最右节点。

2. it指向的当前节点,如果右子树为空,那么分两种情况:

① it指向的当前节点是父节点的右,那么就表明父节点的右子树已经访问完了,接下来直接访问父节点即可。

② it指向的当前结点是父节点的左,那么就表明以父节点为根的这棵子树全部访问完毕,需要访问下一棵子树,那么就要往上找孩子是父亲右的那个祖先节点。

代码就不实现了,只需对operator++代码稍微改动即可。 

3.3 其他接口

operator*、operator->、operator==、operator!=

// 红黑树迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef __TreeIterator<T, Ref, Ptr> Self;typedef RBTreeNode<T> Node;Node* _node;__TreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){//......}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};

4. 添加红黑树的迭代器

根据我们上面实现的operator++原理来看的话:begin()是最左节点, end()是nullptr,那么为什么end()是nullptr呢?我们接下来看一下:

还是这一棵红黑树

当我们访问到了最后一个节点27时,它的左右子树都为空,并且它是父亲节点的右子树,再进行一步it++时,它就会往上面找孩子是父亲左的祖先节点,因此就会一直循环往上走,直到走到了13,但是13为根节点,没有父亲节点。

因此,当访问最后一个节点27时,再进行it++,找到的就是nullptr,所以需要将end()设置为nullptr。

代码实现:
//红黑树的实现
// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<K, T>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{
public:typedef __TreeIterator<T, T&, T*> iterator;typedef __TreeIterator<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);}public:typedef RBTreeNode<T> Node;//插入bool Insert(const T& data)//......//中序遍历void InOrder(){_InOrder(_root);cout << endl;}//判断是否平衡//......//高度//......//节点个数//......//查找//......
private://判断是否平衡//......//右单旋//......//左单旋//......
private:Node* _root = nullptr;
};

5. set的封装

在封装之前我们需要了解一个内嵌类型的细节:

对类模板取内嵌类型需要加上typename 

因为编译器在编译的时候不确定是模板还是类型,不能取到内嵌类型,加上typename就表明是一个类型。

根据set的属性:数据是不允许修改的,所以我们直接将普通迭代器和const迭代器都封装为const迭代器:

typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

5.1 set的插入 

set和map的插入的返回值是一个pair,其中pair第一个参数是迭代器,插入成功表示新插入节点的迭代器,插入失败表示已存在节点的迭代器,第二个参数是bool值,表示插入是否成功。

那么我们就需要对我们的Insert再稍作调整:

我们使用pair的特性,用一个Node*类型的去构造一个iterator类型,这样就保证了iterator到const_iterator的转化:

set封装代码:

#pragma once
#include "RBTree.h"namespace ywh
{template<class K>class set{public://提取set的keystruct 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;public:iterator begin() const{return _tree.begin();}iterator end() const{return _tree.end();}pair<iterator, bool> insert(const K& key){_tree.Insert(key);}private:RBTree<K, K, SetKeyOfT> _tree;};
}

6. map的封装 

map的封装需要实现operator[],返回的pair的second,需要借助于Insert。

6.1 operator[]

关于operator[]的详细介绍和原理都在【C++】:set和map 中,需要的可以去看一看。

        V& operator[](const K& key){pair<iterator, bool> ret = Insert(make_pair(key, V()));return ret.first->second;}

map封装代码:

#pragma once
#include "RBTree.h"namespace ywh
{template<class K, class V>class map{public://提取map的keystruct 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;public:iterator begin(){return _tree.begin();}const_iterator begin() const{return _tree.begin();}iterator end(){return _tree.end();}const_iterator end() const {return _tree.end();}pair<iterator, bool> insert(const pair<K, V>& kv){return _tree.Insert(kv);}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> _tree;};
}

7. 添加迭代器之后的红黑树完整代码

#pragma once//枚举定义节点颜色
enum Color 
{RED,    //红色BLACK   //黑色
};//红黑树节点的定义
template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;    //左子树节点RBTreeNode<T>* _right;   //右子树节点RBTreeNode<T>* _parent;  //父节点Color _col;              //节点颜色T _data;                 //节点的数据//节点的构造RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED),_data(data){}
};// 红黑树迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef __TreeIterator<T, Ref, Ptr> Self;typedef RBTreeNode<T> Node;Node* _node;__TreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->_right)  //右子树不为空,下一个节点就是右子树的最左节点{Node* cur = _node->_right;while (cur->_left){cur = cur->_left;}_node = cur;}else               //右子树为空,下一个节点就是孩子为父亲左的那个祖先节点{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;	}return *this;}Self& operator--(){if (_node->_left)  //左子树不为空,下一个节点就是右子树的最右节点{Node* cur = _node->left;while (cur->_right){cur = cur->_right;}_node = cur;}else               //左子树为空,下一个节点就是孩子为父亲右的那个祖先节点{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};//红黑树的实现
// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<K, T>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{
public:typedef __TreeIterator<T, T&, T*> iterator;typedef __TreeIterator<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);}public:typedef RBTreeNode<T> Node;//插入//pair<iterator, bool> Instrt(const T& data)pair<Node*, bool> Insert(const T& data){//为空可以直接插入,并将根节点置为黑色if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* cur = _root;Node* parent = nullptr;KeyOfT kot;//不为空找到合适的插入位置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 make_pair(cur, false);}//链接//新插入的节点默认为红色节点cur = new Node(data);Node* newnode = cur;  //保存当前节点防止丢失cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}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;//将grandfather变为新的cur继续向上处理cur = grandfather;parent = cur->_parent;}else  //叔叔节点不存在或者存在且为黑{if (cur == parent->_left)   //该路径的parent已经是grandfather的左{//旋转+变色Rotate_right(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else                       //cur是parent的右{//双旋+变色Rotate_left(parent);Rotate_right(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else //parent == grandfather->_right{Node* uncle = grandfather->_left;//叔叔节点存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//将grandfather变为新的cur继续向上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right)   //该路径的parent已经是grandfather的右{//旋转+变色Rotate_left(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else                        //cur是parent的左{Rotate_right(parent);Rotate_left(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}//将根节点再次置黑保持红黑树的平衡_root->_col = BLACK;return make_pair(newnode, true);}//中序遍历void InOrder(){_InOrder(_root);cout << endl;}//判断是否平衡bool IsBalance(){if (_root == nullptr)return true;//1.判断根是否为黑if (_root->_col == RED)return false;int standard_val = 0;  	//最左路径的黑色节点个数Node* cur = _root;while (cur){if (cur->_col == BLACK)standard_val++;cur = cur->_left;}int Black_size = 0;return Check(_root,standard_val,Black_size);}//高度int Height(){return _Height(_root);}//节点个数size_t Size(){return _Size(_root);}//查找Node* 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 cur;}}return NULL;}
private:size_t _Size(Node* root){if (root == NULL)return 0;return _Size(root->_left)+ _Size(root->_right) + 1;}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;}//判断是否平衡bool Check(Node* root, const int standard_val, int Black_size){if (root == nullptr){if (Black_size != standard_val)   //比较黑色节点的个数{cout << "存在黑色节点数量不相等的路径" << endl;return false;}elsereturn true;}//判断它与它父亲的颜色if (root->_col == RED && root->_parent->_col == RED){cout << "有连续的红色节点" << endl;return false;}//黑色节点计数器++if (root->_col == BLACK){Black_size++;}//递归它的左右子树return Check(root->_left, standard_val, Black_size)&& Check(root->_right, standard_val, Black_size);}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}//右单旋void Rotate_right(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 (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;subL->_parent = ppNode;}else{ppNode->_right = subL;subL->_parent = ppNode;}}}//左单旋void Rotate_left(Node* parent){Node* subR = parent->_right;  Node* subRL = subR->_left; Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;parent->_right = subRL;if (subRL)subRL->_parent = parent;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (ppNode->_left == parent)ppNode->_left = subR;elseppNode->_right = subR;subR->_parent = ppNode;}}
private:Node* _root = nullptr;
};

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!    

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

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

相关文章

《Java 简易速速上手小册》第9章:Java 开发工具和框架 (2024 最新版)

文章目录 9.1 Maven 和 Gradle - 构建与依赖管理的神兵利器9.1.1 基础知识9.1.2 重点案例&#xff1a;使用 Maven 构建 Spring Boot 应用9.1.3 拓展案例 1&#xff1a;使用 Gradle 构建多模块项目9.1.4 拓展案例 2&#xff1a;利用 Gradle Wrapper 确保构建的一致性 9.2 Spring…

专业130+总分420+厦门大学847信号与系统考研经验厦大信息系统与通信工程,真题,大纲,参考书。

今年很幸运被厦门大学录取&#xff0c;考研专业课847信号与系统130&#xff0c;数二130&#xff0c;总分420&#xff0c;回头看这将近一年的复习&#xff0c;还是有不少经验和大家分享&#xff0c;希望对大家复习有帮助。专业课&#xff1a; 厦门大学847信号与系统在全国各高校…

C++实现二分查找

目录 例1 例2 例3 例4 例5 例6 例1 704. 二分查找 注意&#xff1a; ①left < right,这里的号是最后一次通过下标mid来判断 ②在偶数的时候mid&#xff0c;左右无所谓&#xff0c;因为left和right都有1&#xff1b; 参考代码 class Solution { public:int search…

阿里(淘天)一面笔试算法原题

阿里撤资 "车来了" 近日&#xff0c;国内实时公交产品"车来了"关联公司武汉元光科技有限公司发生工商变更&#xff0c;阿里巴巴&#xff08;中国&#xff09;网络技术有限公司退出股东行列。 这很好理解&#xff0c;符合近期阿里收缩战线的行为一致性。 毕…

c语言container理解

最近看到一个宏定义如下&#xff1a; /** * rt_container_of - return the member address of ptr, if the type of ptr is the * struct type. */ #define rt_container_of(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->mem…

微信小程序(四十四)鉴权组件插槽-登入检测

注释很详细&#xff0c;直接上代码 新增内容&#xff1a; 1.鉴权组件插槽的用法 2.登入检测示范 源码&#xff1a; app.json {"usingComponents": {"auth":"/components/auth/auth"} }app.js App({globalData:{//定义全局变量isLoad:false} })…

【动态规划】【数学】【C++算法】1449. 数位成本和为目标值的最大数字

作者推荐 【深度优先搜索】【树】【图论】2973. 树中每个节点放置的金币数目 本文涉及知识点 动态规划汇总 LeetCode1449. 数位成本和为目标值的最大数字 给你一个整数数组 cost 和一个整数 target 。请你返回满足如下规则可以得到的 最大 整数&#xff1a; 给当前结果添加…

文件包含知识点详细总结

如果想看图片和观感更好的话,可以直接去我的github或者gitbook github:https://github.com/kakaandhanhan/cybersecurity_knowledge_book-gitbook.22kaka.fun gitbook:http://22kaka.fun description: 这里将通过参考文章和做题一起进行总结,并且文件包含漏洞,很多都利用了…

【Linux】进程概念(冯诺依曼体系结构、操作系统、进程)-- 详解

一、冯诺依曼体系结构 1、概念 &#xff08;1&#xff09;什么是冯诺伊曼体系结构&#xff1f; 数学家冯诺伊曼于 1946 年提出存储程序原理&#xff0c;把程序本身当作数据来对待&#xff0c;程序和该程序处理的数据用同样的方式储存。 冯诺伊曼理论的要点是&#xff1a;计算…

【COMP337 LEC1】

Data Preprocessing Phase 数据预处理 1. Feature extraction 特征提取 1. An object is described by a collection of attributes 一个对象可以由一组特征来描述 2. A feature is a property or a characteristic of an objects 物体的属性 2. Data cleaning 数据清洗 Extra…

【EAI 019】Eureka: Human-Level Reward Design via Coding LLM

论文标题&#xff1a;Eureka: Human-Level Reward Design via Coding Large Language Models 论文作者&#xff1a;Yecheng Jason Ma, William Liang, Guanzhi Wang, De-An Huang, Osbert Bastani, Dinesh Jayaraman, Yuke Zhu, Linxi Fan, Anima Anandkumar 作者单位&#xff…

【ES6】Promise

Promise 回调地狱 const fs require(fs);fs.readFile(./a.txt, utf-8, (err, data) > {if(err) throw err;console.log(data);fs.readFile(./b.txt, utf-8, (err, data) > {if(err) throw err;console.log(data);fs.readFile(./c.txt, utf-8, (err, data) > {if(er…

pm2启动的node项目访问不了,npm start却可以访问

netstat -ntlp输入该命令&#xff0c;查看启动的服务端口是否有被监听到&#xff0c;如3001&#xff0c;4000之类的&#xff0c;是node项目启动时候自己配的那个&#xff0c; 若没有&#xff0c;则执行 pm2 delete [app-id/app-name] 先删除启动的这个项目 例如pm2 delete my…

第三百二十二回

文章目录 1. 概念介绍2. 使用方法2.1 基本用法2.2 缓冲原理 3. 示例代码4. 内容总结 我们在上一章回中介绍了"FadeInImage组件"相关的内容&#xff0c;本章回中将介绍CachedNetworkImage组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章…

《Java 简易速速上手小册》第7章:Java 网络编程(2024 最新版)

文章目录 7.1 网络基础和 Java 中的网络 - 揭开神秘的面纱7.1.1 基础知识7.1.2 重点案例&#xff1a;实现一个简单的聊天程序7.1.3 拓展案例 1&#xff1a;使用 UDP 进行消息广播7.1.4 拓展案例 2&#xff1a;建立一个简单的 Web 服务器 7.2 创建客户端和服务器 - 构建沟通的桥…

Netty源码系列 之 FastThreadLocal源码

目录 Netty优化方案之 FastThreadLocal 前言 ThreadLocal ThreadLocal是干什么的&#xff1f; 为什么要使用ThreadLocal工具类去操控存取目标数据到Thread线程 &#xff1f; ThreadLocal的使用场景 目标数据存储到Thread线程对象的哪里&#xff1f; 怎么样把一个目标数据…

学习Android的第六天

目录 Android TextView 文本框 TextView 基础属性 范例 带阴影的TextView 范例 带边框的TextView 范例 带图片(drawableXxx)的TextView 范例1 范例2 使用autoLink属性识别链接类型 范例 TextView 显示简单的 HTML 范例1 范例2 SpannableString & Spannable…

政安晨:演绎在KerasCV中使用Stable Diffusion进行高性能图像生成

小伙伴们好&#xff0c;咱们今天演绎一个使用KerasCV的StableDiffusion模型生成新的图像的示例。 考虑计算机性能的因素&#xff0c;这次咱们在Colab上进行&#xff0c;Colab您可以理解为在线版的Jupyter Notebook&#xff0c;还不熟悉Jupyter的的小伙伴可以去看一下我以前的文…

python+ flask+MySQL旅游数据可视化81319-计算机毕业设计项目选题推荐(免费领源码)

摘要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对旅游数据可视化等问题&#xff0c;对旅游数据…

图表自动化开篇

目录 前言&#xff1a; 使用 Canvas 或者 SVG 渲染 选择哪种渲染器 代码触发 ECharts 中组件的行为 前言&#xff1a; 图表自动化一直以来是自动化测试中的痛点&#xff0c;也是难点&#xff0c;痛点在于目前越来越多公司开始构建自己的BI报表平台但是没有合适的自动化测试…