map_set(红黑树封装)

1.map和set的整体大致架构

1.map和set的整体

平时我们使用map和set时,头文件是map和set的头文件
set头文件:
在这里插入图片描述
map头文件
在这里插入图片描述
而stl_tree.h代表的就是红黑树

1.2 map和set的大致架构

map和set在源代码基本结构
map的大致特点:
在这里插入图片描述

set的大致特点:
在这里插入图片描述
tree的大致特点:
在这里插入图片描述
红黑树tree的节点数据
在这里插入图片描述

2. 封装map和set的底层实现

2.1实现架构

分为三个头文件保存,RBTree.h,Mymap.h,Myset.h
RBTree.h存放模拟实现的红黑树
Mymap.h存放模拟实现的map
Myset.h存放模拟实现的set

应用泛型模板
基本框架如下
在这里插入图片描述
如图,由于Myset和Mymap中的存放的数据类型是不一样,map中的时pair类型键值对,set存放的是key。
set和map的数据是给出实例化,去对应RBTree中的T类型,最后对应RBTreeNode的节点数据,来让编译器编译出来存放两个数据类型不同的红黑树封装出来map和set

2.2 RBTree.h

2.2.1 节点数据类和节点颜色定义

节点颜色定义:用枚举类型来定义

// 枚举
enum Colour
{RED,BLACK
};

节点数据类定义:模板来定义,来让map和set实例化,来在这里给与T相应的参数,来实现map和set

// 节点类
// 数据直接用一个来代替
template<class T>
struct RBTreeNode
{RBTreeNode<T>* _parent;// 父亲节点RBTreeNode<T>* _left;// 左孩子节点RBTreeNode<T>* _right;// 右孩子节点T _data;// 节点数据Colour _col;// 节点颜色// 构造函数RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};

2.2.2 红黑树的迭代器

    1. begin()与end()
      STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?
      因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置,但是由于这次是模拟,我们先粗略模拟实现,就把end()的位置定义为nullptr
      begin()和end()在迭代器模板类中先不实现,迭代器模板写完之后,而在其他类中电泳迭代器类对象,之后写出红黑树,map,set相应的begin()和end()。
    1. operator++()和operator–()
      a.operator++()
      所要满足的条件:
      1.当前节点的右子树不为空,中序下一个访问的节点,右子树的最左节点
      2.当前节点的右子树为空,下一个访问,倒着在祖先里面找,找孩子是父亲左孩子的祖先

      在这里插入图片描述
// ++重载
// 左子树 根 右子树
// 1.当前节点的右子树不为空,中序下一个访问的节点,右子树的最左节点
// 2.当前节点的右子树为空,下一个访问,倒着在祖先里面找,找孩子是父亲左孩子的祖先
Self& operator++()
{// 判断右子树是否为空// 右节点存在if (_node->_right){// 查找右子树的最左节点Node* leftMin = _node->_right;// 查找while (leftMin->_left){leftMin = leftMin->_left;}// 再将最左节点在赋值给_node_node = leftMin;}else// 右子树不存在{// 倒着在祖先里面找,找孩子是父亲左孩子的祖先// 用cur来查找Node* cur = _node;// 记录父亲节点Node* parent = _node->_parent;// cur是右节点时则要进循环// 也要判断是否到根节点时的情况,parent为空while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}// 找到的祖先赋值给_node,根节点时,则直接返回空_node = parent;}// 直接返回++后的_nodereturn *this;
}

b.operator–()
所要满足的条件:
–it,与++方法相反 右子树 根节点 左子树
1.左不为空,左子树中序的最后一个节点(最右节点)
2.左为空,下一个访问,找孩子是父亲右的那个祖先节点

// --重载
// 则和++重载方法颠倒过来,就是反方向看,右子树 根 左子树
// 1.当前节点左子树存在时,下一个访问节点,查找左子树的最大节点
// 2.当前节点左子树不存在时,下一个访问节点,倒着往回走查找祖先,查找孩子是父亲右的祖先
Self& operator--()
{// 判断左孩子是否存在if (_node->_left)// 当前节点左子树存在时{Node* rightMost = _node->_left;// 一直查找到叶子节点while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else// 当前节点左子树不存在时{Node* cur = _node;Node* parent = cur->_parent;// 倒着往回走查找祖先,查找孩子是父亲右的祖先// 结束条件:循环到根节点或者cur是parent的有孩子节点while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = cur;}return *this;
}

总体迭代器

// 红黑树迭代器
// 给数据节点封装迭代器
//	类型	data     T&       T*
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
public:// 相关重定义typedef RBTreeNode<T> Node;// 节点重定义// 这里只是一个模板,而__RBTreeIterator红黑树的迭代器实现模板里面的类型应在RBTree传递相应参数typedef __RBTreeIterator<T, Ref, Ptr> Self;// 迭代器重定义// 构造函数__RBTreeIterator(Node* node):_node(node){}// 解引用*// T&Ref operator*(){return _node->_data;}// 解引用->// T*Ptr operator->(){return &_node->_data;}// !=bool operator!=(const Self t){return _node != t._node;// 只用比较地址}// ==bool operator==(const Self t){return _node == t._node;}// ++重载// 左子树 根 右子树// 1.当前节点的右子树不为空,中序下一个访问的节点,右子树的最左节点// 2.当前节点的右子树为空,下一个访问,倒着在祖先里面找,找孩子是父亲左孩子的祖先Self& operator++(){// 判断右子树是否为空// 右节点存在if (_node->_right){// 查找右子树的最左节点Node* leftMin = _node->_right;// 查找while (leftMin->_left){leftMin = leftMin->_left;}// 再将最左节点在赋值给_node_node = leftMin;}else// 右子树不存在{// 倒着在祖先里面找,找孩子是父亲左孩子的祖先// 用cur来查找Node* cur = _node;// 记录父亲节点Node* parent = _node->_parent;// cur是右节点时则要进循环// 也要判断是否到根节点时的情况,parent为空while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}// 找到的祖先赋值给_node,根节点时,则直接返回空_node = parent;}// 直接返回++后的_nodereturn *this;}// --重载// 则和++重载方法颠倒过来,就是反方向看,右子树 根 左子树// 1.当前节点左子树存在时,下一个访问节点,查找左子树的最大节点// 2.当前节点左子树不存在时,下一个访问节点,倒着往回走查找祖先,查找孩子是父亲右的祖先Self& operator--(){// 判断左孩子是否存在if (_node->_left)// 当前节点左子树存在时{Node* rightMost = _node->_left;// 一直查找到叶子节点while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else// 当前节点左子树不存在时{Node* cur = _node;Node* parent = cur->_parent;// 倒着往回走查找祖先,查找孩子是父亲右的祖先// 结束条件:循环到根节点或者cur是parent的有孩子节点while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = cur;}return *this;}private:Node* _node;};

2.2.3 红黑树定义

插入:
插入中右一个要比较数据大小的阶段,但由于map中存放的数据是键值对pair<key,value>,而set中存放的数据是key,所以我吗用用一个同意分模板规范他们,使之让他们实例化出来相应的数据。
解决方案:map和set中用各自相应的仿函数返回相应的数据类型
在这里插入图片描述

// 红黑树类
// 由于要封装map和set,但是两个存放的数据类型不一样
// set存放的数据是Key,map存放的数据是pair<Key,Value>
// 所以将数据类型定义为如下//        key      value     数据比较仿函数
template<class K, class T, class KeyOft>
class RBTree
{
public:// 重定义typedef RBTreeNode<T> Node;// 迭代器typedef __RBTreeIterator<T, T&, T*> Iterator;// 普通迭代器typedef __RBTreeIterator<T, const T&, const T*> Const_Iterator;// const迭代器// 这个里面也是一个模板。为map和set封装提供// Begin// 查找红黑树最小值Iterator Begin(){Node* leftMin = _root;while (leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);}// End// 由于没有哨兵位// 先置为空Iterator End(){return Iterator(nullptr);}// 这个里面也是一个模板。为map和set封装提供// Begin// 查找红黑树最小值Const_Iterator Begin() const{Node* leftMin = _root;while (leftMin->_left){leftMin = leftMin->_left;}return Const_Iterator(leftMin);}// End// 由于没有哨兵位// 先置为空Const_Iterator End() const{return Const_Iterator(nullptr);}// 构造函数RBTree() = default;// 拷贝构造// 拷贝只能老老实实的拷贝,不能插入拷贝// 插入拷贝时,旋转可能会影响红黑树和原本的红黑树不一样RBTree(const RBTree<K, T, KeyOft>& t){_root = Copy(t._root);}// 赋值重载// t2 = t1RBTree<K, T, KeyOft> operator=(RBTree<K, T, KeyOft> t){swap(_root, t._root);return *this;}// 析构函数~RBTree(){Destroy(_root);_root = nullptr;}// 插入// 插入一个data// 返回值是一个pair类型,pair第一个参数是Iterator,第二个bool判断是否插入成功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* cur = _root;Node* parent = nullptr;while (cur){// 由于set和map的数据不一样,数据的比较不好比较// key// pair<key,value>// 两个数据类型都有着自己的比较方式// 在map和set中增加一个仿函数// 用一个仿函数的对象来调用,返回相应类型的数据if (kot(data) > kot(cur->_data)){parent = cur;cur = cur->_right;}else if (kot(data) < kot(cur->_data)){parent = cur;cur = cur->_left;}else{return make_pair(Iterator(cur), false);}}cur = new Node(data);Node* newnode = cur;// 记录一下cur的位置,可能旋转之后不知道cur的位置// 新插入节点,插入红色,可能违反规则三// 插入黑色,必然违反规则四// 所以新增节点为红色cur->_col = RED;if (kot(cur->_data) < kot(parent->_data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;// 进行红黑树颜色调整// 分为三种情况:// 1.情况一: 新增cur为红,p为红,g为黑,u存在且为红// 2.情况二: 新增cur为红,p为红,g为黑,u不存在/u存在且为黑// 3.情况三: 新增cur为红,p为红,g为黑,u不存在/u存在且为黑// 情况2和3可以合并为一种进行// uncle有大方向,uncle存在、uncle不存在、uncle存在且wei// 以上情况的解决方案一般parent的颜色是黑色就结束了// 或者为根节点时,parent为空跳出循环while (parent && parent->_col == RED){// grandfather节点Node* grandfather = parent->_parent;// 先看uncle和parent节点的关系// parent节点是grandfather节点的左孩子if (parent == grandfather->_left){// uncle为右孩子Node* uncle = grandfather->_right;// 由于都是uncle节点在变化,关键看uncle节点// 看uncle是否存在,uncle节点的颜色// 情况一: 新增cur为红,p为红,g为黑,u存在且为红// 解决方案:p,u变为黑色,g变为红色// 如果为根节点,则g不变,g的parent为空会跳出循环,在循环外面更改_root节点if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上调整,直到调整到根节点// cur找g,g找g的祖父cur = grandfather;parent = cur->_parent;}else// 其他情况,u为空,或者u节点存在并且为黑色{// 情况2、3:新增cur为红,p为红,g为黑,u不存在/u存在且为黑// 用cur新增节点插入在parent的左右孩子,来将情况2,3分开// cur在左孩子if (cur == parent->_left){//     g  //   p   u// c // 进行右单旋RotateR(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else// cur在左孩子{//      g  //   p     u//     c // 进行左右旋转RotateL(parent);RotateR(grandfather);// 变色cur->_col = BLACK;grandfather->_col = RED;}break;}}else// parent节点是grandfather节点的右孩子{// uncle为左孩子Node* uncle = grandfather->_left;// 情况一: 新增cur为红,p为红,g为黑,u存在且为红// 解决方案:p,u变为黑色,g变为红色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 向上调整cur = grandfather;parent = cur->_parent;}else{// 情况2、3:新增cur为红,p为红,g为黑,u不存在/u存在且为黑// 用cur新增节点插入在parent的左右孩子,来将情况2,3分开// cur在右孩子if (cur == parent->_right){//	 g// u   p//       c// 进行左单旋RotateL(grandfather);//变色parent->_col = BLACK;grandfather->_col = RED;}else{//	  g// u     p//     c// 进行右左单旋RotateR(parent);RotateL(grandfather);grandfather->_col = RED;cur->_col = BLACK;}// 旋转之后直接跳出循环break;}}}// 将根节点颜色变为黑色_root->_col = BLACK;return make_pair(Iterator(newnode), true);}// 右单旋void RotateR(Node* grandfather){Node* parent = grandfather->_left;Node* subR = parent->_right;grandfather->_left = subR;if (subR != nullptr){subR->_parent = grandfather;}parent->_right = grandfather;Node* ppNode = grandfather->_parent;grandfather->_parent = parent;// parent的节点最后与上面判断// 判断根节点if (ppNode == nullptr){_root = parent;}else{// parent在ppnode的哪个孩子if (ppNode->_right == grandfather){ppNode->_right = parent;}else{ppNode->_left = parent;}parent->_parent = ppNode;}}// 左旋转void RotateL(Node* grandfather){Node* parent = grandfather->_right;Node* subL = parent->_left;grandfather->_right = subL;if (subL != nullptr){subL->_parent = grandfather;}Node* ppNode = grandfather->_parent;grandfather->_parent = parent;parent->_left = grandfather;// 判断是否为根节点if (ppNode == nullptr){_root = parent;}else{// parent在ppnode的哪个孩子if (ppNode->_right == grandfather){ppNode->_right = parent;}else{ppNode->_left = parent;}parent->_parent = ppNode;}}// 查找// 返回值类型是迭代器类型// 用key来查找位置Iterator Find(const K& key){// 从头节点开始查找Node* cur = _root;// 结束条件是while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return Iterator(cur);}}return Iterator(nullptr);}// 中序排序void InOrder(){_InOrder(_root);}bool IsBalance(){// 判断根节点是否符合要求if (_root->_col == RED){return false;}int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){refNum++;}cur = cur->_left;}return Check(_root, 0, refNum);}private:// 拷贝// 返回值也是Node*// 拷贝构造:深拷贝,应用递归拷贝Node* Copy(Node* root){// 判断空表时if (root == nullptr){return nullptr;}// 创建一个节点,拷贝当前节点Node* newroot = new Node(root->_data);newroot->_col = root->_col;// 颜色拷贝// 拷贝左子树Copy(root->_left);// 左子树不为空时,左子树的_parent指向newrootif (newroot->_left){newroot->_left->_parent = newroot;}// 拷贝右子树Copy(root->_right);if (newroot->_right){newroot->_right->_parent = newroot;}}// 销毁红黑树// 销毁顺序:左子树,右子树,根节点// 防止先销毁根节点,找不着其他的节点位置void Destroy(Node* root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}//检查平衡基本bool Check(Node* root, int blackNum, const int refNum){// 判断为空if (root == nullptr){//cout << blackNum << endl;// 判断路径中的黑色节点if (blackNum != refNum){cout << "存在黑色节点的数量不相等的路径" << endl;return false;}return true;}// 1.不能有连续的红色节点,遍历红色节点,检查红色节点的父亲节点是不是红色if (root->_col == RED && root->_parent->_col == RED){// cout << root->_kv.first << "存在有连续的红色节点" << endl;return false;}// 2.每条路径黑色节点的数量,每一个黑色节点,记录一下// 根节点到当前路径黑色节点的数量,任意计算一条路径当左参考值,之后与其他路径比较if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum) && Check(root->_left, blackNum, refNum);}// 中序排序基本void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}Node* _root = nullptr;
};

2.3 Mymap.h

map就是一个红壳,只用调用红黑树当中的方法

// map是Key,pair<key,value>模型
namespace lc
{// Key,Value模型template<class K, class V>class map{// 由于map和set的数据不一样// 写一个仿函数,用来给与插入比较数据struct MapKeyOft{// 返回值为K类型const K& operator()(const pair<K, V>& kv){return kv.first;}};public:// 重定义// 引入红黑树迭代器// 未实例化的类模板,不知道迭代器的类型,在前面加上typename解决typedef typename RBTree<K, pair<K, V>, MapKeyOft>::Iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOft>::Const_Iterator const_iterator;// 普通迭代器// 直接点调用迭代器中的Beginiterator begin(){return _t.Begin();}// 调用模板End()iterator end(){return _t.End();}// const迭代器// 直接点调用迭代器中的Beginconst_iterator begin() const{return _t.Begin();}// 调用模板End()const_iterator end() const{return _t.End();}// 插入// 调用红黑树的插入方法// 这里的map插入相应的数据参数pair<iterator,bool> Insert(const pair<K, V>& kv){return _t.Insert(kv);}// 查找iterator Find(const K& key){_t.Find(key);}// []重载// 用参数first找到second// 返回值得到secondV& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}private:// 成员变量就是一个红黑树RBTree<K, pair<K, V>, MapKeyOft> _t;};}

map的测试用例

void test_map1(){map<string, int> m;m.Insert({ "苹果",1 });m.Insert({ "香蕉",1 });m.Insert({ "梨",1 });m.Insert({ "苹果",3 });map<string, int>::iterator it = m.begin();while (it != m.end()){//it->first += 'x';it->second += 1;//cout << it.operator->()->first << ":" << it->second << endl;cout << it->first << ":" << it->second << endl;++it;}cout << endl;}void test_map2(){string arr[] = { "苹果", "香蕉", "梨", "苹果", "草莓", "苹果", "草莓",
"香蕉", "香蕉", "梨", "梨","梨","梨", "草莓","草莓" };map<string, int> countMap;for (auto& e : arr){countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}cout << endl;}

2.4 Myset.h

set只是相当于一个空壳,只用调用相应的红黑树中的方法

// set存放的是K,K模型
// 底层都是红黑树
namespace lc
{template<class K>class set{// 由于map和set的数据不一样// 写一个仿函数,用来给与插入比较数据struct SetKeyOft{const K& operator()(const K& key){return key;}};public:// 重定义// 引入红黑树迭代器// Iterator未实例化的类模板,不知道他的类型,在前面加上typename解决typedef typename RBTree<K, K, SetKeyOft>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOft>::Const_Iterator const_iterator;// 直接点调用迭代器中的Beginiterator begin(){return _t.Begin();}// 调用模板End()iterator end(){return _t.End();}// const迭代器const_iterator begin() const{return _t.Begin();}// 调用模板End()const_iterator end() const{return _t.End();}// 插入// 调用红黑树的插入方法// 这里的map插入相应的数据参数pair<iterator, bool> Insert(const K& key){return _t.Insert(key);}// 查找iterator Find(const K& key){_t.Find(key);}private:RBTree<K, K, SetKeyOft> _t;};

set测试用例:

void test_set1(){set<int> s;s.Insert(4);s.Insert(2);s.Insert(5);s.Insert(15);s.Insert(7);s.Insert(1);s.Insert(5);s.Insert(7);set<int>::iterator it = s.begin();while (it != s.end()){// *it += 5;cout << *it << " ";// 测试--/*if (*it == 7){--it;cout << *it << endl;break;}*/++it;}cout << endl;/*for (auto e : s){cout << e << endl;}*//*set<int> copy = s;for (auto e : copy){cout << e << " ";}cout << endl;*/// cout << copy._t.IsBalance() << endl;}}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Linux gcc/g++ _ make/makefile

文章目录 库gcc/g程序编译过程链接动态链接静态链接 make _ makefile 库 一、 什么是库&#xff1f; 库是程序代码的集合&#xff0c;是共享程序代码的一种方式。根据源代码的公开情况&#xff0c;库可以分为两种类型&#xff1a; 开源库&#xff0c;公开源代码&#xff0c;能…

SPICE | 常见电路SPICE模型总结

Ref. 1. CMOS VLSI Design: A Circuits and Systems Perspective 目录 0 基础 1 反相器 inverter 2 缓存器 buffer 3 NAND 4 NOR 5 传输门 Transmission gate 6 三态反相器 Tristate Inverter 7 选择器 Multiplexers 8 D锁存器 D Latch 9 D触发器 D Flip-Flop 0 基础…

数模·微分方程

微分方程 核心概念 含导数的方程或方程组 通解和特解的区别&#xff1a;有初值条件的通解称作特解 解析解和数值解的&#xff1a;解析解是通过代数或解析方法得到的精确解。它通常以闭式表达式或公式的形式存在&#xff1b;数值解是通过数值方法&#xff08;如迭代算法&#x…

了解Java虚拟机(JVM)

前言&#x1f440;~ 上一章我们介绍网络原理相关的知识点&#xff0c;今天我们浅浅来了解一下java虚拟机JVM JVM&#xff08; Java Virtual Machine &#xff09; JVM内存区域划分 方法区/元数据区&#xff08;线程共享&#xff09; 堆&#xff08;线程共享&#xff09; 虚…

数据结构——二叉树性质

性质1:在二叉树的第i层上至多有2^(i-1)个结点(i>1)。 这个性质很好记忆&#xff0c;观察一下图6-5-5。 第一层是根结点&#xff0c;只有一个&#xff0c;所以2^(1-1)2^01。 第二层有两个&#xff0c;2^(2-1)22。 第三层有四个&#xff0c;2^(3-1)2^24。 第四层有八个&am…

土地规划与水资源管理:和谐共生,共绘绿色发展的生态蓝图

在快速城市化与气候变化的双重挑战下&#xff0c;土地规划与水资源管理的协同成为了确保可持续发展的关键。本文旨在深入探讨如何将水资源管理融入土地规划的各个环节&#xff0c;以实现资源高效利用与环境的和谐共生。 一、水资源的现状与挑战 全球水资源分布不均&#xff0…

react-native从入门到实战系列教程一环境安装篇

充分阅读官网的环境配置指南&#xff0c;严格按照他的指导作业&#xff0c;不然你一直只能在web或沙箱环境下玩玩 极快的网络和科学上网&#xff0c;必备其中的一个较好的心理忍受能力&#xff0c;因为上面一点就可以让你放弃坚持不懈&#xff0c;努力尝试 成功效果 三大件 …

AI绘画;喂饭进阶!教你如何用Stable Diffusion生成高清建筑手工模型图,一篇文章搞懂什么是Lora模型和CKPT主模型!

前言 刚接触Stable Diffusion不久的你&#xff0c;是否有这样的疑问&#xff1a; Q1: Stable Diffusion中的主模型CKPT是什么&#xff1f; Q2: Stable Diffusion中的Lora模型又是什么&#xff1f; Q3: 在哪儿可以下载好用的AI绘图模型&#xff1f; Q4: Stable Diffusion 如…

Linux---01---安装VMware

一. 什么时Linux Linux 是一个开源的类 Unix 操作系统,Linux 是许多计算机硬件的底层操作系统&#xff0c;特别是服务器、嵌入式系统和个人电脑。它支持多种架构&#xff0c;包括 x86、x64、ARM 和 MIPS 等。Linux 因其稳定性、安全性、开源性以及广泛的社区支持而广受欢迎。 …

【Linux】文件系统|CHS寻址|LBA逻辑块|文件索引|inode|Date block|inodeBitmap|blockBitmap

前言 一个进程通过文件描述符标识一个打开的文件&#xff0c;进程拿着文件描述符可以在内核中找到目标文件进行读写等操作。这是打开的文件&#xff0c;而没有被打开的文件存储在磁盘中&#xff0c;是如何管理的&#xff1f;操作系统在偌大的磁盘中如何找到想要的文件并打开的…

【有哪些GPU算力租用平台值得推荐】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

开放式耳机会成为未来的主流吗?开放式耳机推荐指南

开放式耳机是否会成为未来的主流&#xff0c;是一个值得探讨的问题。 从目前的市场趋势和技术发展来看&#xff0c;有一些因素支持开放式耳机可能成为主流。 一方面&#xff0c;人们对于健康和舒适的关注度不断提高。长时间佩戴传统耳机可能导致耳部不适&#xff0c;而开放式…

java通过poi解析word入门

文章目录 介绍一、了解word docx文档的结构二、引入POI的依赖三、解析Word文档常用API加载Word文档获取文档整体结构获取文档中的段落获取文档中的表格获取文档中的脚注 四、解析Word中的段落示例五、读取Word文档并遍历图片六、解析Word中的图片示例 介绍 Apache POI 是一个处…

AI绘画入门实践|Midjourney:使用 --no 去除不想要的物体

在 Midjourney 中&#xff0c;--no 作为反向提示词&#xff0c;告诉 MJ 在生成图像时&#xff0c;不要包含什么。 使用格式&#xff1a;--no 对应物体提示词&#xff08;多个物体之间使用","间隔&#xff09; 使用演示 a web banner, summer holiday --v 6.0 a web b…

[MySQL][深入理解隔离性][下][Read View]详细讲解

目录 1.Read View1.是什么&#xff1f;2.理解3.整体流程 2.RR与RC的本质区别1.当前读和快照读在RR级别下的区别2.RR与RC的本质区别 1.Read View 1.是什么&#xff1f; Read View就是事务进行 快照读 操作的时候生产的 读视图(Read View)&#xff0c;在该事务执行快照读的那一…

C语言 #指针数组 #数组指针 #数组参数、指针参数

文章目录 前言 一、指针数组 1、概念&#xff1a; 2、指针数组有什么用呢&#xff1f; 二、数组指针 1、数组指针的定义 2、数组名与 &数组名 的区别 3、数组指针如何初始化&#xff1f; 4、数组指针的用法 三、根据代码区分 指针数组 和 数组指针 四、数组参数、指针参数 …

VLAN通讯实验

目录 拓扑图 需求 需求分析 配置过程 1、手工配置 2、 使用DHCP获得IP地址信息 3、测试全网是否可达 拓扑图 需求 1、PC1、PC3属于VLAN 2 2、PC2、PC4属于VLAN 3 3、通过DHCP使得PC获取IP地址信息 4、全网可达 需求分析 1、先手工配置网段&#xff0c;VLAN 2为192.168.1…

在invidia jetpack4.5.1上运行c++版yolov8(tensorRT)

心路历程(可略过) 为了能在arm64上跑通yolov8,我试过很多很多代码,太多对库版本的要求太高了; 比如说有一个是需要依赖onnx库的,(https://github.com/UNeedCryDear/yolov8-opencv-onnxruntime-cpp) 运行成功了报错error: IOrtSessionOptionsAppendExecutionProvider C…

【网络安全的神秘世界】文件包含漏洞

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 一、概述 文件包含&#xff1a;重复使用的函数写在文件里&#xff0c;需要使用某个函数时直接调用此文件&#xff0c;而无需再…

使用代理IP进行本地SEO优化:如何吸引附近的客户?

在今天竞争激烈的互联网时代&#xff0c;如何利用代理IP进行本地SEO优化并吸引附近的客户已经成为许多企业和网站面临的关键挑战。本文将探讨使用代理IP的策略和技巧&#xff0c;以帮助公司提高在本地市场的可见性和吸引力&#xff0c;从而扩大本地客户群体。 1. 代理IP在本地…