Map与Set的模拟实现封装

目录

一.   底层原理

 二.   红黑树节点的定义

三.   仿函数封装

四.   基本函数的封装

五.   迭代器的封装

5.1   迭代器的基本定义

5.2   *与->操作

5.3  迭代器的++操作

5.3.1   右子树不为空

5.3.2   右子树为空 

5.4   迭代器的--操作

5.4.1   当前节点的父节点为空

 5.4.2   左子树不为空

5.4.3   左子树为空

5.5   ==与!=操作

六.   Map整体封装

七.   Set整体封装

八.   红黑树整体封装


一.   底层原理

        我们需要知道的是Map和Set底层是由红黑树封装的。而我们红黑树的底层又是kv结构。那我们可以把红黑树的V变成Map和Set传参的地方,Map传的是Key,Set传的是pair<Key,value>

因此我们可以为了识别到底是Map还是Set定义一个模板参数T

template<class K,class T>
class RBTree{};

此处参数K依旧是Key,只不过参数T可以是Set的Key,也可以是Map的pair<Key,Value>

如果是Map,那么传参就是:

template<class K,class T>
class Map
{
private:RBTree<K, pair<K,T>> _map;
};

而如果是Set,那么传参就是:

template<class K>
class Set
{
private:RBTree<K,K> _set;
};

我们可以看见,无论是Map还是Set,好像T参数已经包含了K参数,那为什么还要第一个参数K参数呢?

因为我们除了去insert(const Value& v)以外,还有find(const Key& k)操作,而find函数就需要第一个参数K,而如果不要第一个参数Set是不能满足的,所以第一个参数是必需的。

 二.   红黑树节点的定义

这里节点的定义我们与前面普通的红黑树(具体的定义可看:http://t.csdnimg.cn/hlYqJ)不一样的是,我们需要去考虑到底是Map还是Set,也就是传的参数不一样。所以可以用一个模板参数来定义:

enum Colour
{RED,BLACK
};
template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;T _data;RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED),_data(data){}
};

此处T,Map就传pair<Key,Value>,而Set就传Key。

三.   仿函数封装

我们可以看见对于Map的pair我们是不能做比较,也做不了比较的,但是我们可以知道的是Key是能做比较的,因此我们需要将pair中的Key取出来作比较,这里就能用到我们的仿函数。

仿函数(functor)是一种在C++中使用的概念,它允许一个类的对象表现得像函数一样。仿函数通过在其类定义中重载函数调用运算符operator()来实现这种行为。

  • 对于Map我们需要取出pair键值对中的第一个元素Key。
namespace yjy {template<class K,class T>class Map{struct MapKeyOfT{const K& operator()(const pair<K,T>& kt){return kt.first;}};private:RBTree<K, pair<K,T>, MapKeyOfT> _map;};
}
  • 对于Set我们直接返回自带的Key就行。
namespace yjy {template<class K>class Set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};private:RBTree<K,K, SetKeyOfT> _set;};
}

 整体的仿函数传参即:

 那么有了我们的仿函数之后,我们就可以运用在下面这样的比较之中:

bool Find(const T& data)
{KeyOfT _rot;Node* cur = _root;while (cur){if (_rot(cur->_data) < _rot(data)){cur = cur->_right;}else if (_rot(cur->_data) > _rot(data)){cur = cur->_left;}else{return true;}}return false;
}

四.   基本函数的封装

我们有了仿函数之后,就可以对一些基本操作函数进行编写(此处只是在红黑树的基础上加上了仿函数,如果对操作还有不懂的,可以去看:http://t.csdnimg.cn/577bU)。

bool Find(const T& data)
{KeyOfT _rot;Node* cur = _root;while (cur){if (_rot(cur->_data) < _rot(data)){cur = cur->_right;}else if (_rot(cur->_data) > _rot(data)){cur = cur->_left;}else{return true;}}return false;
}
bool Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}KeyOfT _rot;Node* parent = nullptr;Node* cur = _root;while (cur){if (_rot(cur->_data) > _rot(data)){parent = cur;cur = cur->_left;}else if (_rot(cur->_data) < _rot(data)){parent = cur;cur = cur->_right;}else{return true;}}cur = new Node(data);Node* newnode = cur;if (_rot(parent->_data) < _rot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandparent = parent->_parent;Node* uncle = nullptr;//parent和uncle是grandparent的左还是右不影响结果//cur是parent的左还是右不影响结果if (parent == grandparent->_left){uncle = grandparent->_right;//uncle存在且为红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;if (grandparent == _root){grandparent->_col = BLACK;}else{cur = grandparent;parent = cur->_parent;}}else{//		g//	p		u//cif (cur == parent->_left){RotaleR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}//		g//	p		u//		celse{RotaleL(parent);RotaleR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}else{uncle = grandparent->_left;if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;if (grandparent == _root){grandparent->_col = BLACK;}else{cur = grandparent;parent = cur->_parent;}}else{//		g//	u		p//				cif (cur == parent->_right){RotaleL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}//		g//	u		p//		celse{RotaleR(parent);RotaleL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}return true;
}
void RotaleL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL){subRL->_parent = parent;}Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subR;}else if (parent == ppnode->_right){ppnode->_right = subR;}subR->_parent = ppnode;}
}
void RotaleR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else if (ppnode->_right == parent){ppnode->_right = subL;}subL->_parent = ppnode;}
}

五.   迭代器的封装

我们写出了仿函数之后,一切都水到渠成了,就可以继续对迭代器进行封装了。

5.1   迭代器的基本定义

template<class T,class Ptr,class Ref>//此处Ptr是T*,Ref是T&
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T,Ptr,Ref> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}
}

5.2   *与->操作

对于*操作,就是返回数据的引用,而->操作,就是返回数据的地址,即指针。

Ref operator*()
{return _node->_data;
}
Ptr operator->()
{return &_node->_data;
}

5.3  迭代器的++操作

此处我们需要分为右子树不为空和右子树为空两种情况。为什么呢?

我们可以根据二叉树的中序遍历来看,根节点遍历完了,就该遍历右子树,如果右子树为空,则直接跳到上一层,如果不为空,则进入右子树。那么我们下面来细讲一下这两种情况。 

Self& operator++()
{if (_node->_right)//右边不为空{//右子树的最左边节点Node* subright = _node->_right;while (subright->_left){subright = subright->_left;}_node = subright;}else{//祖先里面孩子是父亲左的那个Node* cur = _node;Node* subparent = _node->_parent;while (subparent&&subparent->_right == cur){cur = subparent;subparent = subparent->_parent;}_node = subparent;}return *this;
}

5.3.1   右子树不为空

 我们可以根据中序来解释,进入右子树之后,我们应该进入右子树的最左节点。

 如图,当前节点是50,右子树不为空,则走到右子树的最左节点56。

5.3.2   右子树为空 

当右子树为空的情况出现时,我们可以知道后面一步需要遍历到当前节点的父节点,那么我们再进一步思考一下,当前节点是父节点的右节点时,又说明父节点的右子树遍历完了,又需要向上迭代。所以我们要迭代到什么时候才行呢?

应该是迭代到当前节点是父节点的左节点时,此时后面一步就是到父节点。

如图:当前节点是48,右节点为空,则向上走,一直走到35的时候,此时35是50的左节点。 

5.4   迭代器的--操作

--操作与++操作不同,不只是迭代的方向不同,情况也有所不同。

我们这里要先判断当前节点的父节点是否为空节点。为什么呢?咱们下面再说。除了这种情况外,还有左子树不为空和左子树为空两种情况。

Self& operator--()
{if (_node->_parent==nullptr){Node* maxright = _node;while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else if (_node->_left)//左边不为空{//左子树的最右边节点Node* subright = _node->_left;while (subright->_right){subright = subright->_right;}_node = subright;}else{//祖先里面孩子是父亲右的那个Node* cur = _node;Node* subparent = _node->_parent;while (subparent && subparent->_left == cur){cur = subparent;subparent = subparent->_parent;}_node = subparent;}return *this;
}

5.4.1   当前节点的父节点为空

当前节点的父节点为空,证明当前节点是此时的根节点。我们再进行--的话,就要走到最右边节点。因为在STL库定义中,是如下图一样的结构:

我们这里就没有定义header头结点,但是我们还是可以看到,根节点之后应该到最右节点

 5.4.2   左子树不为空

此时根据++操作右子树不为空时的情况可以得到此时应该走到左子树的最右节点

如图:当前节点是50,此后应该迭代到左子树的最右节点,即48。 

5.4.3   左子树为空

此时也应该向上迭代,到什么时候结束呢?

应该到当前节点是父节点的右节点为止,因为如果当前节点是父节点的左节点时,又说明左子树走完了,又要向上迭代。所以我们要一直迭代到当前节点是父节点的右节点时。

 

如图:当前节点是40,应该迭代到当前节点是父节点的右节点时,所以要迭代到45。 

5.5   ==与!=操作

对于==与!=操作就是判断数据是否相等。

bool operator!=(const Self& s)
{return _node != s._node;
}
bool operator==(const Self& s)
{return _node == s._node;
}

六.   Map整体封装

对于Map来说,需要多一个operator[]操作。

由于我们知道,Map里面的Key是不能随意改变的,所以加上const修饰

namespace yjy {template<class K,class T>class Map{struct MapKeyOfT{const K& operator()(const pair<K,T>& kt){return kt.first;}};public:typedef typename RBTree<K, pair<const K,T>, MapKeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K,T>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _map.begin();}iterator end(){return _map.end();}const_iterator begin() const{return _map.begin();}const_iterator end() const{return _map.end();}pair<iterator,bool> Insert(const pair<K,T>& kt){return _map.Insert(kt);}iterator Find(const K& k){return _map.Find(k);}T& operator[](const K& key){pair<iterator, bool> ret = Insert(make_pair(key, T()));return ret.first->second;}private:RBTree<K, pair<const K,T>, MapKeyOfT> _map;};
}

七.   Set整体封装

此处的Key也要加上const修饰,因为是不可改变的。

namespace yjy {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>::const_iterator const_iterator;iterator begin(){return _set.begin();}iterator end(){return _set.end();}const_iterator begin() const{return _set.begin();}const_iterator end() const{return _set.end();}pair<iterator, bool> Insert(const K& key){return _set.Insert(key);}iterator Find(const K& k){return _set.Find(k);}private:RBTree<K,const K, SetKeyOfT> _set;};
}

八.   红黑树整体封装

#include<iostream>
#include<vector>
using namespace std;
enum Colour
{RED,BLACK
};
template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;T _data;RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED),_data(data){}
};
template<class T,class Ptr,class Ref>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T,Ptr,Ref> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->_right)//右边不为空{//右子树的最左边节点Node* subright = _node->_right;while (subright->_left){subright = subright->_left;}_node = subright;}else{//祖先里面孩子是父亲左的那个Node* cur = _node;Node* subparent = _node->_parent;while (subparent&&subparent->_right == cur){cur = subparent;subparent = subparent->_parent;}_node = subparent;}return *this;}Self& operator--(){if (_node->_parent==nullptr){Node* maxright = _node;while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else if (_node->_left)//左边不为空{//左子树的最右边节点Node* subright = _node->_left;while (subright->_right){subright = subright->_right;}_node = subright;}else{//祖先里面孩子是父亲右的那个Node* cur = _node;Node* subparent = _node->_parent;while (subparent && subparent->_left == cur){cur = subparent;subparent = subparent->_parent;}_node = subparent;}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>
//Map->RBTree<K,pair<K,V>,MapKeyOfT>//KeyOfT仿函数,取出T对象中的key
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;iterator begin(){Node* subleft = _root;while (subleft&&subleft->_left){subleft = subleft->_left;}return iterator(subleft);}const_iterator begin() const{Node* subleft = _root;while (subleft && subleft->_left){subleft = subleft->_left;}return const_iterator(subleft);}iterator end(){return iterator(nullptr);}const_iterator end() const{return const_iterator(nullptr);}iterator Find(const T& data){KeyOfT _rot;Node* cur = _root;while (cur){if (_rot(cur->_data) < _rot(data)){cur = cur->_right;}else if (_rot(cur->_data) > _rot(data)){cur = cur->_left;}else{return iterator(cur);}}return end();}pair<iterator,bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root), true);}KeyOfT _rot;Node* parent = nullptr;Node* cur = _root;while (cur){if (_rot(cur->_data) > _rot(data)){parent = cur;cur = cur->_left;}else if (_rot(cur->_data) < _rot(data)){parent = cur;cur = cur->_right;}else{return make_pair(iterator(cur),true);}}cur = new Node(data);Node* newnode = cur;if (_rot(parent->_data) < _rot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandparent = parent->_parent;Node* uncle = nullptr;//parent和uncle是grandparent的左还是右不影响结果//cur是parent的左还是右不影响结果if (parent == grandparent->_left){uncle = grandparent->_right;//uncle存在且为红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;if (grandparent == _root){grandparent->_col = BLACK;}else{cur = grandparent;parent = cur->_parent;}}else{//		g//	p		u//cif (cur == parent->_left){RotaleR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}//		g//	p		u//		celse{RotaleL(parent);RotaleR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}else{uncle = grandparent->_left;if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;if (grandparent == _root){grandparent->_col = BLACK;}else{cur = grandparent;parent = cur->_parent;}}else{//		g//	u		p//				cif (cur == parent->_right){RotaleL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}//		g//	u		p//		celse{RotaleR(parent);RotaleL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}return make_pair(iterator(newnode),true);}void RotaleL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL){subRL->_parent = parent;}Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subR;}else if (parent == ppnode->_right){ppnode->_right = subR;}subR->_parent = ppnode;}}void RotaleR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else if (ppnode->_right == parent){ppnode->_right = subL;}subL->_parent = ppnode;}}
private:Node* _root=nullptr;
};

总结

好了,到这里今天的知识就讲完了,大家有错误一点要在评论指出,我怕我一人搁这瞎bb,没人告诉我错误就寄了。

祝大家越来越好,不用关注我(疯狂暗示)

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

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

相关文章

Qt中连接mysql

1、安装mysql&#xff0c;workbench&#xff0c;为mysql添加环境变量 2、安装Qt带src&#xff0c;然后到如下目录&#xff0c;找到mysql.pro(建议做个副本先) http://D:\Qt\Qt5.13.2\5.13.2\Src\qtbase\src\plugins\sqldrivers\mysql mysql.pro 注意路径的 \ / 和双引号的使…

Rust腐蚀服务器修改背景和logo图片操作方法

Rust腐蚀服务器修改背景和logo图片操作方法 大家好我是艾西一个做服务器租用的网络架构师。在我们自己搭建的rust服务器游戏设定以及玩法都是完全按照自己的想法设定的&#xff0c;如果你是一个社区服那么对于进游戏的主页以及Logo肯定会有自己的想法。这个东西可以理解为做一…

stm32实现hid键盘

前面的cubelmx项目配置参考 stm32实现hid鼠标-CSDN博客https://blog.csdn.net/anlog/article/details/137814494?spm1001.2014.3001.5502两个项目的配置完全相同。 代码 引用 键盘代码&#xff1a; 替换hid设备描述符 先屏蔽鼠标设备描述符 替换为键盘设备描述符 修改宏定…

vagrant 安装虚拟机,docker, k8s

第一步&#xff1a;安装虚拟机 1、安装 vagrant 本机是 mac, 但是这一步不影响&#xff0c;找对应操作系统的安装方式就行了。 vagrant 下载地址 brew install vagrant 2、下载 VirtualBox 虚拟机 VirtualBox 下载地址 找到对应系统下载&#xff0c;安装就可以。 尽量把…

stm32f103--按键播放音乐

目录 一、了解一下延时函数 二、准备音频数据表 三、了解一下蜂鸣器 四、音乐奏响原理 &#xff08;1&#xff09;了解一个音调中的更加细致的小声音 &#xff08;2&#xff09;发出一个音调的声音 五、按键发出声音 一、了解一下延时函数 1s1000ms1000000us 如果是1us…

vue-treeselect 的基本使用

vue-treeselect 的基本使用 1. 效果展示2. 安装 插件3. 引入组件4. 代码 1. 效果展示 2. 安装 插件 vue-treeselect是一个树形的下拉菜单&#xff0c;至于到底有多少节点那就要看你的数据源有多少层了&#xff0c;挺方便的。下面这个这个不用多说吧&#xff0c;下载依赖 npm in…

基本模拟概念

目标&#xff1a; 讨论模拟电子技术的基本特性 描述模拟信号 分析信号源 解释放大器的特性 1.1模拟电子学 电子学可以划分成很多的分类来研究。其中最基本的一种分类方式是将信号分成可由 二进制数字表示的数字信号和由连续变化量表示的模拟信号。数字电子学包括所有的算术 和…

MAC安装CocoaPods遇到的错误Failed to build gem native extension.

MAC安装CocoaPods遇到的错误Failed to build gem native extension. 配置flutter环境的时候报错cocoapods不可用 发现已经安装了CocoaPods&#xff0c;但是不能用 重新安装CocaPods sudo gem install cocoapods重新安装报错如下&#xff1a; 安装RVM curl -L https://get.r…

第十一章数据仓库和商务智能10分

【数据仓库-后端&#xff0c;商务智能-前端】 基本算法&#xff1a;关联关系&#xff08;牵手-谈恋爱&#xff09;&#xff0c;集群关系&#xff08;杭州人爱吃酸甜口&#xff09;&#xff0c;决策树&#xff0c;线性回归&#xff0c;贝叶斯&#xff0c;神经网络&#xff0c;时…

为什么说日本茶道源于中国茶文化

茶&#xff0c;始于药&#xff0c;而后为饮茶&#xff0c;始于药&#xff0c;而后为饮。在8世纪的中国&#xff0c;茶就作为一桩雅事而进入一个诗意王国。而日本则在15世纪将其尊崇为一种美的宗教——茶道。 茶道&#xff0c;是在日常染污之间&#xff0c;因由对美的倾慕而建立…

redis五种类型介绍

Redis是一种内存数据存储系统&#xff0c;它支持五种不同的数据类型&#xff1a; 1. String String是Redis中最基本的数据类型&#xff0c;它可以存储任何形式的字符串数据&#xff0c;例如普通的文本字符串&#xff0c;二进制数据或JSON格式的数据。除此之外&#xff0c;还可以…

快手本地生活服务商入驻方法来了!超简单

本地生活市场正如一座蕴藏丰富的金矿&#xff0c;亟待我们去挖掘其潜在的价值。在2023年这个消费市场全面回暖的年份&#xff0c;服务零售行业的增速犹如一匹黑马&#xff0c;远远超过了商品零售。据权威数据显示&#xff0c;服务零售额的增长幅度高达20%&#xff0c;比商品消费…

前端开发框架BootStrap

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl BootStrap概述 Bootstrap是一个开源的前端框架&#xff0c;它由Twitter的设计师和开发者创建并维护。Bootstrap提供了许多现成的Web组件&#xff0c;可帮助开发者快速设计和…

Spring (四) 之配置及配置文件的操作

文章目录 1、Spring 基于注解的配置基于注解的配置引入依赖包配置实体类数据访问层业务层业务层实现测试 2、Bean和Component和Configuration的区别1 Bean:2 Component:3 Configuration:总结&#xff1a; 区别Component和Configuration区别 3、Spring读取properties配置文件准备…

第一届AI Agent智能体现场开发大赛报名开启!8月上旬火热开赛~

由联想拯救者、AIGC开放社区、英特尔携手主办的“AI生成未来第二届拯救者杯OPENAIGC开发者大赛”已经正式启动&#xff0c;“2024 AI Agent极限挑战赛”作为特设专项赛道&#xff0c;也将同步于8月上旬开赛&#xff0c;参赛者将在更加紧张刺激的现场比赛中展现其技术与创造力。…

抖音小店新店铺起飞式玩法,这几步一定要做好,前期很重要

大家好&#xff0c;我是电商笨笨熊 进入抖音小店后不知道该怎么操作&#xff0c;不清楚如何让新店快速起店&#xff1b; 今天我们就来聊聊新店铺快速起店的几个关键步骤&#xff0c;新手玩家一定要按照流程去做。 第一步&#xff1a;店铺搭建 小店开通之后不要着急选品上架&…

MGRE环境下的ospf实验

MGRE环境下的ospf实验 一.拓扑图 二.实验步骤 1.分配各路由网段IP [R1]int g 0/0/0 [R1-GigabitEthernet0/0/0]ip address 16.0.0.1 24 [R1-GigabitEthernet0/0/0]int g 0/0/1 [R1-GigabitEthernet0/0/1]ip address 116.0.0.1 24[R2]int g 0/0/0 [R2-GigabitEthernet0/0/0]…

Docker+Uwsgi部署Django项目

在之前的文章中&#xff0c;已经给大家分享了在docker中使用django自带的命令部署项目&#xff0c;这篇文章主要讲解如何使用uwsgi部署。 1. 在Django项目的根目录下新建Dockerfile文件 #Dockerfile文件 # 使用 Python 3.9 作为基础镜像 FROM python:3.9# 设置工作目录 WORKDI…

随机游走的艺术-图嵌入表示学习

图嵌入引入 机器学习算法&#xff1a; 厨师 样本集&#xff1a; 食材 只有好的食材才能做出好的饭菜 我们需要把数据变成计算机能够读懂的形式&#xff08;将数据映射成为向量&#xff09; 图嵌入概述 传统图机器学习 图表示学习 自动学习特征&#xff0c;将…

初学python记录:力扣924. 尽量减少恶意软件的传播

题目&#xff1a; 给出了一个由 n 个节点组成的网络&#xff0c;用 n n 个邻接矩阵图 graph 表示。在节点网络中&#xff0c;当 graph[i][j] 1 时&#xff0c;表示节点 i 能够直接连接到另一个节点 j。 一些节点 initial 最初被恶意软件感染。只要两个节点直接连接&#x…