C++——mapset红黑树

目录

一补充知识

1关联式容器

2键值对

二set

1set的介绍

2set的使用

三map 

1map的说明

 2map的使用

四容器在oj中的使用

五AVL树

1概念 

2插入

3AVL的旋转

 3.1右单旋

3.2左单旋

3.3左右双旋

3.4右左双旋

 4判断AVL树是否平衡

完整源代码 

六红黑树 

1概念

2性质/规则

3插入

①p为红色,u为红色

 ②p为红色,u不存在

③p为红色,u为黑色 

代码实现 

4判断是否平衡 

完整源代码

七map和set的封装 

1基本框架

​编辑 2红黑树的迭代器

​编辑 2.1operator++

3find 

4insert和map的operator[]


一补充知识

1关联式容器

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高

2键值对

通常用pair<class K,class V>来表示具有一一对应关系的一种结构:

该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。

比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义

二set

1set的介绍

1. set是按照一定次序存储元素的容器(升序)
2. 在set中,元素的value也标识它(value就是key,类型为T),每个value必须是唯一的(去重)
set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
3. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
子集进行直接迭代。
4. set在底层是用红黑树实现的

2set的使用

关于set的接口,有需要自己跳转到链接:set接口说明 


set的模板参数列表:

T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较

#include <set>
void TestSet()
{// 用数组array中的元素构造setint array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,6, 8, 0 };set<int> s(array, array + sizeof(array) / sizeof(array));cout << s.size() << endl;// 正向打印set中的元素,从打印结果中可以看出:set可去重for (auto& e : s)cout << e << " ";cout << endl;// 使用迭代器逆向打印set中的元素for (auto it = s.rbegin(); it != s.rend(); ++it)cout << *it << " ";cout << endl;// set中值为3的元素出现了几次cout << s.count(3) << endl;
}

在set中,还存在着一个key模型搜索:multiset:

#include<set>
int main()
{// key模型搜索// 排序 不去重,允许冗余multiset<int> s1;s1.insert(1);s1.insert(11);s1.insert(3);s1.insert(4);s1.insert(2);s1.insert(4);s1.insert(2);s1.insert(1);s1.insert(2);s1.insert(1);multiset<int>::iterator it = s1.begin();while (it != s1.end()){//*it = 1;cout << *it << " ";++it;}cout << endl;return 0;
}

三map 

1map的说明

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
value_type绑定在一起

3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
5. map通常被实现为红黑树。

关于pair的结构体,stl对它的定义: 

template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
};

 2map的使用

map的插入与打印:

#include<map>
int main()
{map<string, string> dict;pair<string, string> kv1("sort", "排序");dict.insert(kv1);dict.insert(pair<string, string>("left", "左边"));dict.insert(make_pair("right", "右边"));// 隐式类型转换//pair<string, string> kv2 = { "string", "字符串" };dict.insert({ "string", "字符串" });//map<string, string>::iterator it = dict.begin();auto it = dict.begin();while (it != dict.end()){// iterator key不能修改 value可以修改// const_iterator key不能修改 value不能修改//it->first += 'x';it->second += 'x';//cout << (*it).first << ":" << (*it).second << endl;cout << it->first << ":" << it->second << endl;//省略了一个箭头//cout << it.operator->()->first << ":" << it.operator->()->second << endl;++it;}cout << endl;for (auto& kv : dict){//auto& [x, y] = kv;cout << kv.first << ":" << kv.second << endl;}cout << endl;/*for (auto& [x, y] : dict){cout << x << ":" << y << endl;}cout << endl;*/return 0;
}

运算符重载[]的使用:共有几种情况

#include<map>
int main()
{map<string, string> dict;dict.insert({ "string","字符串" });// 插入(一般不会这么用)dict["right"];cout << dict["right"] << endl;// 插入+修改dict["left"] = "左边";cout << dict["left"] << endl;// "查找"cout << dict["string"] << endl;// 修改dict["right"] = "右边";cout << dict["right"] << endl;return 0;
}

四容器在oj中的使用

前k个高频单词:oj链接

两个数组的交集:oj链接

五AVL树

1概念 

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右
子树高度之差的绝对值不超过1(需要对树中的结点进行调整)
,即可降低树的高度,从而减少平均搜索长度。

AVL树具有以下性质:

它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

平衡因子=右子树高度 - 左子树高度

2插入

aAVL树本身也是二叉搜索树,按照搜索树的规则进行插入;

b插入在父亲的左边,平衡因子--;插入在父亲的右边,平衡因子++;

c考虑父亲的平衡因子

==0,父亲所在的子树高度不变,不在往上进行更新了;

==1/-1,父亲所在的子树高度变了,要往上进行更新;

==2/-2,父亲所在的子树已经不平衡了,要进行旋转处理;

bool Insert(const pair<A, V>& key)
{if (root == nullptr){root = new Node(key);return true;}Node* _parent = nullptr;Node* cur = root;while (cur){if (cur->_v.first > key.first){_parent = cur;cur = cur->left;}else if (cur->_v.first < key.first){_parent = cur;cur = cur->right;}else{//不存在return false;}}//cur位置是插入新节点的位置cur = new Node(key);//插入在左还是右if (_parent->_v.first > key.first){//小的在左边_parent->left = cur;}else if (_parent->_v.first < key.first){//大的在右边_parent->right = cur;}cur->parent = _parent;//调整平衡因子while (_parent){if (_parent->left == cur){//左边增加--_parent->_bf--;}else{//右边增加++_parent->_bf++;}//满足性质不用在进行判断 1->0 -1->0if (_parent->_bf == 0){break;}//高度发生改变要往上进行调节 0 -> 1/-1else if (_parent->_bf == 1 || _parent->_bf == -1){//继续循环cur = _parent;_parent = _parent->parent;}else if (_parent->_bf == 2 || _parent->_bf == -2){//旋转来解决不平衡...}else{assert(false);}}return true;
}

3AVL的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

 3.1右单旋

说明:由于在不同情况下具象图的情况有很多,所有在大多数都是采用抽象图来进行显示:

抽象图: 

 具象图:(h==0的情况) 以抽象图来进行分析:(新插入的节点在60的左子树中

 

以parent为旋转点进行右单旋(顺时针方向进行旋转):

(目的)左子树过高,为了降低左子树的高度使树平衡

void RotateR(Node* _parent)
{Node* subL = _parent->left;Node* subLR = subL->right;_parent->left = subLR;//指向父亲节点的修正if (subLR != nullptr) subLR->parent = _parent;subL->right = _parent;//记录_parent的父亲节点,它的位置不同会有多种情况Node* _pparent = _parent->parent;_parent->parent = subL;//_parent是根节点if (_parent == root){root = subL;subL->parent = nullptr;}//_parent只是子树else{if (_pparent->left == _parent){_pparent->left = subL;}else if (_pparent->right == _parent){_pparent->right = subL;}subL->parent = _pparent;}_parent->_bf = subL->_bf = 0;
}

代码难写在于指向的父亲节点的进行修正!! 

3.2左单旋

如果右子树过高就要进行左单旋:(新插入的节点在30的右子树中

以parent为旋转点进行左单旋(逆时针),降低右子树的高度

void RotateL(Node* _parent)
{Node* subR = _parent->right;Node* subRL = subR->left;_parent->right = subRL;if (subRL != nullptr) subRL->parent = _parent;subR->left = _parent;//进行_parent节点的保留Node* _pparent = _parent->parent;_parent->parent = subR;//如果_parent是根节点if (_parent == root){root = subR;//没有了父节点subR->parent = nullptr;}//草率了//else subR->parent = _parent->parent;//在pparent的左还是右未知else{if (_pparent->left == _parent){_pparent->left = subR;//_pparent->_bf++;上层的bf从来没有改过}else if (_pparent->right == _parent){_pparent->right = subR;//_pparent->_bf--;}subR->parent = _pparent;}_parent->_bf = subR->_bf = 0;
}

如果左单旋或者右单旋无法解决问题,那么就要考虑使用两者的结合组成双旋

3.3左右双旋

抽象图的三个节点的位置类似数学符号<:遇到这种情况要进行左右双旋

但是:在更新平衡因子的时候会有三种不同的情况: 

第一种:自己就是新增: 

第二种:插入在左边: ​

第三种:插入在右边: 

三种不同的情况决定我们接下来写出的代码在平衡因子中要表现出来!! 

void RotateLR(Node* _parent)
{//调整平衡因子Node* subL = _parent->left;Node* subLR = subL->right;int bf = subLR->_bf;RotateL(_parent->left);RotateR(_parent);//左边新增if (bf == -1){subLR->_bf = 0;subL->_bf = 0;_parent->_bf = 1;}//右边新增else if (bf == 1){subLR->_bf = 0;subL->_bf = -1;_parent->_bf = 0;}//自己就是新增else if (bf == 0){subLR->_bf = subL->_bf = _parent->_bf = 0;}else{assert(false);}
}

3.4右左双旋

与左右双旋的情况是反过来的:

分析平衡因子也是有三种情况:与左右双旋的情况是类似的,自己动手分析下吧_(:з」∠)_ 

void RotateRL(Node* _parent)
{//调整平衡因子Node* subR = _parent->right;Node* subRL = subR->left;int bf = subRL->_bf;RotateR(subR);RotateL(_parent);//左边新增if (bf == -1){subRL->_bf = 0;subR->_bf = 1;_parent->_bf = 0;}//右边新增else if (bf == 1){subRL->_bf = 0;subR->_bf = 0;_parent->_bf = -1;}//自己就是新增else if (bf == 0){subRL->_bf = 0;subR->_bf = 0;_parent->_bf = 0;}else{assert(false);}
}

 4判断AVL树是否平衡

根据AVL树的性质来进行判断:高度差的绝对值<=1;再随便判断一下平衡因子是否正确


int _Height(Node* root)
{if (root == nullptr) return 0;return max(_Height(root->left), _Height(root->right))+1;
}bool _IsBalance(Node* root)
{if (root == nullptr) return true;int HeightLeft = _Height(root->left);int HeightRight = _Height(root->right);//1高度if (abs(HeightLeft-HeightRight) >= 2) return false;//2平衡因子if (HeightRight - HeightLeft != root->_bf) return false;return _IsBalance(root->left)&&_IsBalance(root->right);}

完整源代码 

#pragma once
#include<utility>
#include<iostream>
#include<assert.h>
using namespace std;
namespace bit
{template<class A, class V>struct Node{Node<A, V>* parent;Node<A, V>* left;Node<A, V>* right;pair<A, V> _v;int _bf;Node(const pair<A, V>& v):parent(nullptr),left(nullptr),right(nullptr),_v(v),_bf(0){}};template<class A, class V>class AVL{typedef Node<A, V> Node;public:Node* find(const A& key){if (root == nullptr) return nullptr;Node* cur = root;while (cur){if (cur->_v.first < key){cur = cur->left;}else if (cur->_v.first > key){cur = cur->right;}else{//找到return cur;break;}}//为空return nullptr;}bool Insert(const pair<A, V>& key){if (root == nullptr){root = new Node(key);return true;}Node* _parent = nullptr;Node* cur = root;while (cur){if (cur->_v.first > key.first){_parent = cur;cur = cur->left;}else if (cur->_v.first < key.first){_parent = cur;cur = cur->right;}else{//不存在return false;}}//cur位置是插入新节点的位置cur = new Node(key);//插入在左还是右if (_parent->_v.first > key.first){//小的在左边_parent->left = cur;}else if (_parent->_v.first < key.first){//大的在右边_parent->right = cur;}cur->parent = _parent;//调整平衡因子while (_parent){if (_parent->left == cur){//左边增加--_parent->_bf--;}else{//右边增加++_parent->_bf++;}//满足性质不用在进行判断 1->0 -1->0if (_parent->_bf == 0){break;}//高度发生改变要往上进行调节 0 -> 1/-1else if (_parent->_bf == 1 || _parent->_bf == -1){//继续循环cur = _parent;_parent = _parent->parent;}else if (_parent->_bf == 2 || _parent->_bf == -2){//左单旋 ->右数太高if (_parent->_bf == 2 && cur->_bf == 1){RotateL(_parent);}//右单旋 ->左数太高else if (_parent->_bf == -2 && cur->_bf == -1){RotateR(_parent);}//左右双旋 ->先以_parent->left为旋转点先进行左单旋else if (_parent->_bf == -2 && cur->_bf == 1){RotateLR(_parent);}//右左双旋else if (_parent->_bf == 2 && cur->_bf == -1){RotateRL(_parent);}break;}else{assert(false);}}return true;}void RotateL(Node* _parent){Node* subR = _parent->right;Node* subRL = subR->left;_parent->right = subRL;if (subRL != nullptr) subRL->parent = _parent;subR->left = _parent;//进行_parent节点的保留Node* _pparent = _parent->parent;_parent->parent = subR;//如果_parent是根节点if (_parent == root){root = subR;//没有了父节点subR->parent = nullptr;}//草率了//else subR->parent = _parent->parent;//在pparent的左还是右未知else{if (_pparent->left == _parent){_pparent->left = subR;//_pparent->_bf++;上层的bf从来没有改过}else if (_pparent->right == _parent){_pparent->right = subR;//_pparent->_bf--;}subR->parent = _pparent;}_parent->_bf = subR->_bf = 0;}void RotateR(Node* _parent){Node* subL = _parent->left;Node* subLR = subL->right;_parent->left = subLR;if (subLR != nullptr) subLR->parent = _parent;subL->right = _parent;Node* _pparent = _parent->parent;_parent->parent = subL;if (_parent == root){root = subL;subL->parent = nullptr;}else{if (_pparent->left == _parent){_pparent->left = subL;}else if (_pparent->right == _parent){_pparent->right = subL;}subL->parent = _pparent;}_parent->_bf = subL->_bf = 0;}void RotateLR(Node* _parent){//调整平衡因子Node* subL = _parent->left;Node* subLR = subL->right;int bf = subLR->_bf;RotateL(_parent->left);RotateR(_parent);//左边新增if (bf == -1){subLR->_bf = 0;subL->_bf = 0;_parent->_bf = 1;}//右边新增else if (bf == 1){subLR->_bf = 0;subL->_bf = -1;_parent->_bf = 0;}//自己就是新增else if (bf == 0){subLR->_bf = subL->_bf = _parent->_bf = 0;}else{assert(false);}}void RotateRL(Node* _parent){//调整平衡因子Node* subR = _parent->right;Node* subRL = subR->left;int bf = subRL->_bf;RotateR(subR);RotateL(_parent);//左边新增if (bf == -1){subRL->_bf = 0;subR->_bf = 1;_parent->_bf = 0;}//右边新增else if (bf == 1){subRL->_bf = 0;subR->_bf = 0;_parent->_bf = -1;}//自己就是新增else if (bf == 0){subRL->_bf = 0;subR->_bf = 0;_parent->_bf = 0;}else{assert(false);}}bool IsBalance(){return _IsBalance(root);}void Inorder(){_Inorder(root);cout << endl;}int Height(){return _Height(root);}int Size(){return _Size(root);}private:bool _IsBalance(Node* root){if (root == nullptr) return true;int HeightLeft = _Height(root->left);int HeightRight = _Height(root->right);//1高度if (abs(HeightLeft-HeightRight) >= 2) return false;//2平衡因子if (HeightRight - HeightLeft != root->_bf) return false;return _IsBalance(root->left)&&_IsBalance(root->right);}void _Inorder(Node* root){if (root == nullptr) return;_Inorder(root->left);cout << root->_v.first << ":" << root->_v.second << ' ';_Inorder(root->right);}int _Height(Node* root){if (root == nullptr) return 0;return max(_Height(root->left), _Height(root->right))+1;//root这一层}int _Size(Node* root){if (root == nullptr) return 0;return _Size(root->left) + _Size(root->right) + 1;}Node* root = nullptr;};void AVLTreeTset1(){AVL<int, int> av;int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){av.Insert({ e,e });cout << "Insert" << ":" << e << "->" << av.IsBalance() << endl;}av.Inorder();cout << av.IsBalance() << endl;}void TestAVLTree2(){const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);//cout << v.back() << endl;}size_t begin2 = clock();AVL<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));//cout << "Insert:" << e << "->" << t.IsBalance() << endl;}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;cout << t.IsBalance() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值for (auto e : v){t.find(e);}//随机值/*for (size_t i = 0; i < N; i++){t.find((rand() + i));}*/size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;}}

六红黑树 

1概念

红黑树:是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,通过颜色来控制平衡。

2性质/规则

1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

3插入

红黑树有红色,黑色可供选择,我们要在进行插入时要选择那个颜色来进行插入?

选择黑色我们必然违反规则四,而选择红色我们有可能违反规则三:这种情况我们可以进行调整来消除:所以在插入时默认选择的是红色:在设计红黑树的节点颜色给红色作为缺省值

 现在我们来分析插入时的三种情况:(用p来代表父节点,u代表父的兄弟节点,g代表祖先节点)

①p为红色,u为红色

在不违反规则四的情况下,将p,u变为黑,g变为红

接下来还要进行对g的判断:g为根节点时,将它再变为黑就结束;g不为根节点时还要再进行处理:

 ②p为红色,u不存在

u不存在时,对p,g进行变色还不够,还要在此基础上进行单旋,保证遵守规则四!!

③p为红色,u为黑色 

出现这种情况一定是下面的①情况变上来的:与第②中情况就的处理方法类似:

p,g进行变色+右单旋(p是g的右孩子就进行左单旋)

上面的三种情况严格来说只有两种情况:u为红处理变色

u为黑/不存在既要变色也要进行旋转,至于是左单旋还是右单旋要看p在g的那边来确定 

而如果要插入的节点在p的右边,根据我们在AVL树学习的知识: g,p,cur组成的形状为<的要进行左右双旋;形状为>的要进行右左双旋

有了上面AVL旋转的分析,看到这里应该不是问题!!

代码实现 

bool Insert(const V& val)
{//根为空if (_root == nullptr){_root = new Node(val);_root->_col = Blank;return true;}//不为空//要记录cur的parent!!(又忘了(⊙﹏⊙))Node* parent = nullptr;Node* cur=_root;while (cur){//比我小往左走if (val.first < cur->_kv.first){parent = cur;cur = cur->_left;}//比我大往右走else if (val.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{//存在相等就失败return false;}}//cur为空:进行插入cur = new Node(val);if (val.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent&& parent->_col == Red){Node* greadfather = parent->_parent;//关键看uncle的位置,情况//1.uncle的位置if (greadfather->_left == parent){Node* uncle = greadfather->_right;//2.uncle有三种情况:为红,不存在,为黑if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Blank;greadfather->_col = Red;//要向上进行搜索cur = greadfather;parent = cur->_parent;}//不存在/为黑else{//以greadfather进行右旋if (parent->_left == cur){//     g//   p   u// cRotateR(greadfather);parent->_col = Blank;greadfather->_col = Red;//uncle为黑也不用管//(这种情况是由前面的uncle为Red变来的)}//在parent右边:以parent进行左旋;以greadfather进行右旋else{//     g//  p     u//     cRotateL(parent);RotateR(greadfather);cur->_col = Blank;greadfather->_col = Red;}break;}}else{Node* uncle = greadfather->_left;//uncle为红if (uncle && uncle->_col == Red){uncle->_col = parent->_col = Blank;greadfather->_col = Red;cur = greadfather;parent = cur->_parent;}//不存在/为黑else{//在右边:以greadgather进行左旋//   g// u   p//       cif (parent->_right == cur){RotateL(greadfather);parent->_col = Blank;greadfather->_col = Red;}//在左边:以parent进行右旋;以greadfather进行左旋else{//     g//  u     p//     cRotateR(parent);RotateL(greadfather);cur->_col = Blank;greadfather->_col = Red;}break;}}}//不管有没有变_root->_col = Blank;return true;
}

4判断是否平衡 

结合红黑树的性质来进行判断:

根节点的判断;是否有连续红节点的判断;每条路径是否有相同黑节点的判断

bool Check(Node* root,int Num,int BlankNum)
{if (root == nullptr){if (Num == BlankNum) return true;else return false;}if (root->_col == Blank) Num++;if (root->_col == Red && root->_parent->_col == Red) return false;return Check(root->_left, Num, BlankNum)&& Check(root->_right, Num, BlankNum);}bool _IsBalance(Node* root)
{if (root->_col == Red) return false;Node* cur = root;int BlankNum = 0;while (cur){if (cur->_col == Blank) BlankNum++;cur = cur->_left;}return Check(root,0,BlankNum);
}bool IsBalance()
{return _IsBalance(_root);
}

完整源代码

enum Colour
{Red,Blank
};namespace RB
{template<class K,class V>struct RBTreeNode{RBTreeNode(const pair<K,V>& val):_parent(nullptr), _left(nullptr), _right(nullptr), _col(Red), _kv(val){}RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;Colour _col;pair<K, V> _kv;//V _kv;};template<class K,class V>class RBTree{public:typedef RBTreeNode<K, V> Node;void RotateL(Node* parent){Node* parentR = parent->_right;Node* parentRL = parentR->_left;parent->_right = parentRL;if (parentRL != nullptr) parentRL->_parent = parent;parentR->_left = parent;//记录Node* pparent = parent->_parent;parent->_parent = parentR;if (_root == parent){_root = parentR;//(⊙o⊙)…parentR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = parentR;}else{pparent->_right = parentR;}parentR->_parent = pparent;}}void RotateR(Node* parent){Node* parentL = parent->_left;Node* parentLR = parentL->_right;parent->_left = parentLR;if (parentLR != nullptr){parentLR->_parent = parent;}parentL->_right = parent;Node* pparent = parent->_parent;parent->_parent = parentL;if (_root == parent){_root = parentL;parentL->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = parentL;}else{pparent->_right = parentL;}parentL->_parent = pparent;}}bool Insert(const pair<K,V>& val){//根为空if (_root == nullptr){_root = new Node(val);_root->_col = Blank;return true;}//不为空//要记录cur的parent!!(又忘了(⊙﹏⊙))Node* parent = nullptr;Node* cur=_root;while (cur){//比我小往左走if (val.first < cur->_kv.first){parent = cur;cur = cur->_left;}//比我大往右走else if (val.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{//存在相等就失败return false;}}//cur为空:进行插入cur = new Node(val);if (val.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent&& parent->_col == Red){Node* greadfather = parent->_parent;//关键看uncle的位置,情况//1.uncle的位置if (greadfather->_left == parent){Node* uncle = greadfather->_right;//2.uncle有三种情况:为红,不存在,为黑if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Blank;greadfather->_col = Red;//要向上进行搜索cur = greadfather;parent = cur->_parent;}//不存在/为黑else{//以greadfather进行右旋if (parent->_left == cur){//     g//   p   u// cRotateR(greadfather);parent->_col = Blank;greadfather->_col = Red;//uncle为黑也不用管//(这种情况是由前面的uncle为Red变来的)}//在parent右边:以parent进行左旋;以greadfather进行右旋else{//     g//  p     u//     cRotateL(parent);RotateR(greadfather);cur->_col = Blank;greadfather->_col = Red;}break;}}else{Node* uncle = greadfather->_left;//uncle为红if (uncle && uncle->_col == Red){uncle->_col = parent->_col = Blank;greadfather->_col = Red;cur = greadfather;parent = cur->_parent;}//不存在/为黑else{//在右边:以greadgather进行左旋//   g// u   p//       cif (parent->_right == cur){RotateL(greadfather);parent->_col = Blank;greadfather->_col = Red;}//在左边:以parent进行右旋;以greadfather进行左旋else{//     g//  u     p//     cRotateR(parent);RotateL(greadfather);cur->_col = Blank;greadfather->_col = Red;}break;}}}//不管有没有变_root->_col = Blank;return true;}void InOrder(){_InOrder(_root);}bool IsBalance(){return _IsBalance(_root);}private:bool Check(Node* root,int Num,int BlankNum){if (root == nullptr){if (Num == BlankNum) return true;else return false;}if (root->_col == Blank) Num++;if (root->_col == Red && root->_parent->_col == Red) return false;return Check(root->_left, Num, BlankNum)&& Check(root->_right, Num, BlankNum);}bool _IsBalance(Node* root){if (root->_col == Red) return false;Node* cur = root;int BlankNum = 0;while (cur){if (cur->_col == Blank) BlankNum++;cur = cur->_left;}return Check(root,0,BlankNum);}void _InOrder(Node* root){if (root == nullptr) return;_InOrder(root->_left);cout << root->_kv.first << ":"<<root->_kv.second<<endl;//cout << Return(root->_kv) << endl;_InOrder(root->_right);}Node* _root = nullptr;};void TestRBTree1(){//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };RBTree<int, int> t1;for (auto e : a){if (e == 10){int i = 0;}// 1、先看是插入谁导致出现的问题// 2、打条件断点,画出插入前的树// 3、单步跟踪,对比图一一分析细节原因t1.Insert({ e,e });cout << "Insert:" << e << "->" << t1.IsBalance() << endl;}t1.InOrder();cout << t1.IsBalance() << endl;}void TestRBTree2(){const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);//cout << v.back() << endl;}size_t begin2 = clock();RBTree<int, int> t;for (auto e : v){t.Insert(make_pair(e,e));//cout << "Insert:" << e << "->" << t.IsBalance() << endl;}size_t end2 = clock();cout << t.IsBalance() << endl;}
}

七map和set的封装 

1基本框架

set和map的valute不同:一个是key,一个是pair<key,valute>;但两者的底层都是通过红黑树来实现的;

思路一:我们可以使用两份相同的红黑树代码,把valute改下进行可以实现目标,但这会使我们代码出现冗余,而且里面有大量重复工作,不是很好的解决办法~-~;

让我们来尝试着通过STL源码来找到map和set的实现思路:

通过类模板自己推演出类型,解决代码冗余的问题

我们来仿照上面的思路,来进行对红黑树的简单改造:

template<class V>
struct RBTreeNode
{RBTreeNode(const V& val):_parent(nullptr), _left(nullptr), _right(nullptr), _col(Red), _kv(val){}RBTreeNode<V>* _parent;RBTreeNode<V>* _left;RBTreeNode<V>* _right;Colour _col;//pair<K, V> _kv;V _kv;
};template<class K, class V>
class RBTree
{
public:typedef RBTreeNode<V> Node;void insert(const V& val)
{Node* parent = nullptr;Node* cur = _root;while (cur){//比我小往左走if (kot(val) < kot(cur->_kv)){parent = cur;cur = cur->_left;}//比我大往右走else if (kot(val) > kot(cur->_kv)){parent = cur;cur = cur->_right;}else{//存在相等就失败return make_pair(Iterator(cur),false);}}....
}private:Node* _root=nullptr;
};//map
template<class K,class V>
class map
{
private:RBTree<K, pair<K,V>> _t;
};//set
class set
{
private:RBTree<K, K> _t;
};

简单改造后,我们会发现一个问题:在插入时我们要与节点上的值进行比较:set比较的是key;map比较的是pair<K,V>中的第一个:

而在实现insert中我们并不知道当前是哪一个容器要进行插入,这要怎么解决呢?

解决:在map和set中各自写一个类来实现valute的返回值,传递给模板:让它自己去推演出要实例化的类型:借助这个类型创建对象,对象在各自去调用对应的类方法来确定返回值!

 2红黑树的迭代器

 如果有学习过list的迭代器并自己独立实现过的话:到了这里应该对你来说不是问题:

template<class V,class Ref,class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<V> Node;typedef RBTreeIterator<V, Ref, Ptr> Slef;Node* _it;RBTreeIterator(Node* it):_it(it){}Ref operator*(){return _it->_kv;}Ptr operator->(){return &_it->_kv;}bool operator!=(const Slef& it){return _it != it._it;}};

但这里的beign()位置可不是根节点,而是左子树的最右节点!

而end()在stl中把它当成header节点,这里我们简单点,把它当成nullptr来使用就行

stl中红黑树结构: 

 2.1operator++

根据搜索二叉树走的是中序遍历来进行分析:共有两种情况:

上面说的可能有点抽象,根据代码来理解理解: 

Slef& operator++()
{//找右子树的最左节点(最小值)if (_it->_right != nullptr){Node* leftMin = _it->_right;while (leftMin->_left){leftMin = leftMin->_left;}_it = leftMin;}//右数为空else{//往上找到parent是祖先节点的左边Node* cur = _it;Node* parent = cur->_parent;//有可能_it是根的情况while (parent && parent->_right == cur){cur = parent;parent = parent->_parent;}_it = parent;}return *this;

实现operator--就是与++的情况反过来的思路,这里就不展开叙述: 

Slef& operator--()
{//找左数的最右节点if (_it->_left != nullptr){Node* left = _it->_left;while (left->_right){left = left->_right;}_it = left;}else{//找是祖先节点的右节点Node* cur = _it;Node* parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_it = parent;}return *this;
}

3find 

 在红黑树的模板中,当时我们传入了K:你可能会想:模板中有V就能够实现绝大部分valute的实例化了,要K有什么用?

其实它是为了实现find函数准备的!!要进行查找一个值用的就是它的K值:

Iterator find(const K& val)
{Node* cur = _root;while (cur){if (cur->_kv > val){cur = cur->_left;}else if (cur->_kv < val){cur = cur->_right;}else{//相等return Iterator(cur);}}return Iterator(nullptr);
}

4insert和map的operator[]

 在stl实现中,insert的返回值的类型为:pair<iterator,bool>

返回值有两种情况:假设插入的值为:cur

1如果值存在了,返回make_pair(cur,false)

2如果不存在,返回make_pair(cur,true)

而map实现operator[]可以通过insert来找到对应的值;即pair<K,V>中的V 

 完整源代码

// RBTree.h
#pragma once
#include<iostream>
using namespace std;
//用枚举来定义颜色方便初始化
enum Colour
{Red,Blank
};namespace bit
{template<class V>struct RBTreeNode{RBTreeNode(const V& val):_parent(nullptr), _left(nullptr), _right(nullptr), _col(Red), _kv(val){}RBTreeNode<V>* _parent;RBTreeNode<V>* _left;RBTreeNode<V>* _right;Colour _col;//pair<K, V> _kv;V _kv;};template<class V,class Ref,class Ptr>struct RBTreeIterator{typedef RBTreeNode<V> Node;typedef RBTreeIterator<V, Ref, Ptr> Slef;Node* _it;RBTreeIterator(Node* it):_it(it){}Ref operator*(){return _it->_kv;}Ptr operator->(){return &_it->_kv;}bool operator!=(const Slef& it){return _it != it._it;}Slef& operator++(){//找右子树的最左节点(最小值)if (_it->_right != nullptr){Node* leftMin = _it->_right;while (leftMin->_left){leftMin = leftMin->_left;}_it = leftMin;}//右数为空else{//往上找到parent是祖先节点的左边Node* cur = _it;Node* parent = cur->_parent;//有可能_it是根的情况while (parent && parent->_right == cur){cur = parent;parent = parent->_parent;}_it = parent;}return *this;}//++思路反过来Slef& operator--(){//找左数的最右节点if (_it->_left != nullptr){Node* left = _it->_left;while (left->_right){left = left->_right;}_it = left;}else{//找是祖先节点的右节点Node* cur = _it;Node* parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_it = parent;}return *this;}};template<class K, class V, class KeyOfT>class RBTree{public:typedef RBTreeNode<V> Node;typedef RBTreeIterator<V, V&, V*> Iterator;typedef RBTreeIterator<V, const V&, const V*> Const_Iterator;Const_Iterator begin() const{//空树的情况Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return cur;}Const_Iterator end() const{return nullptr;}Iterator begin(){//空树的情况Node* cur = _root;while (cur&&cur->_left){cur = cur->_left;}return Iterator(cur);}Iterator end(){return Iterator(nullptr);}//函数三件套RBTree() = default;//拷贝构造RBTree(const RBTree<K, V, KeyOfT>& t){_root = Copy(t._root);}//不用引用RBTree<K, V,KeyOfT>& operator=(RBTree<K, V, KeyOfT> t){swap(t._root, _root);return *this;}~RBTree(){Destory(_root);}void RotateL(Node* parent){Node* parentR = parent->_right;Node* parentRL = parentR->_left;parent->_right = parentRL;if (parentRL != nullptr) parentRL->_parent = parent;parentR->_left = parent;//记录Node* pparent = parent->_parent;parent->_parent = parentR;if (_root == parent){_root = parentR;//(⊙o⊙)…parentR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = parentR;}else{pparent->_right = parentR;}parentR->_parent = pparent;}}void RotateR(Node* parent){Node* parentL = parent->_left;Node* parentLR = parentL->_right;parent->_left = parentLR;if (parentLR != nullptr){parentLR->_parent = parent;}parentL->_right = parent;Node* pparent = parent->_parent;parent->_parent = parentL;if (_root == parent){_root = parentL;parentL->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = parentL;}else{pparent->_right = parentL;}parentL->_parent = pparent;}}//模版的k为它准备的!!Iterator find(const K& val){Node* cur = _root;while (cur){if (cur->_kv > val){cur = cur->_left;}else if (cur->_kv < val){cur = cur->_right;}else{//相等return Iterator(cur);}}return Iterator(nullptr);}pair<Iterator,bool> Insert(const V& val){//根为空if (_root == nullptr){_root = new Node(val);_root->_col = Blank;return make_pair(Iterator(_root), true);}//不为空//要记录cur的parent!!(又忘了(⊙﹏⊙))//类模板传的是类型!!KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){//比我小往左走if (kot(val) < kot(cur->_kv)){parent = cur;cur = cur->_left;}//比我大往右走else if (kot(val) > kot(cur->_kv)){parent = cur;cur = cur->_right;}else{//存在相等就失败return make_pair(Iterator(cur),false);}}//cur为空:进行插入cur = new Node(val);//要返回cur要对cur进行保留Node* newnode = cur;if (kot(val) < kot(parent->_kv)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == Red){Node* greadfather = parent->_parent;//关键看uncle的位置,情况//1.uncle的位置if (greadfather->_left == parent){Node* uncle = greadfather->_right;//2.uncle有三种情况:为红,不存在,为黑if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Blank;greadfather->_col = Red;//要向上进行搜索cur = greadfather;parent = cur->_parent;}//不存在/为黑else{//以greadfather进行右旋if (parent->_left == cur){//     g//   p   u// cRotateR(greadfather);parent->_col = Blank;greadfather->_col = Red;//uncle为黑也不用管//(这种情况是由前面的uncle为Red变来的)}//在parent右边:以parent进行左旋;以greadfather进行右旋else{//     g//  p     u//     cRotateL(parent);RotateR(greadfather);cur->_col = Blank;greadfather->_col = Red;}break;}}else{Node* uncle = greadfather->_left;//uncle为红if (uncle && uncle->_col == Red){uncle->_col = parent->_col = Blank;greadfather->_col = Red;cur = greadfather;parent = cur->_parent;}//不存在/为黑else{//在右边:以greadgather进行左旋//   g// u   p//       cif (parent->_right == cur){RotateL(greadfather);parent->_col = Blank;greadfather->_col = Red;}//在左边:以parent进行右旋;以greadfather进行左旋else{//     g//  u     p//     cRotateR(parent);RotateL(greadfather);cur->_col = Blank;greadfather->_col = Red;}break;}}}//不管有没有变_root->_col = Blank;return make_pair(Iterator(newnode),true);}void InOrder(){_InOrder(_root);}bool IsBalance(){return _IsBalance(_root);}private:Node* _root = nullptr;//前序遍历进行new节点Node* Copy(const Node* root){if (root == nullptr) return nullptr;Node* newroot = new Node(root->_kv);newroot->_col = root->_col;newroot->_left=Copy(root->_left);if (newroot->_left != nullptr) newroot->_left->_parent = newroot;newroot->_right = Copy(root->_right);if (newroot->_right != nullptr) newroot->_right->_parent = newroot;return newroot;}//后序遍历进行析构void Destory(const Node* root){if (root == nullptr) return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}bool Check(Node* root, int Num, int BlankNum){if (root == nullptr){if (Num == BlankNum) return true;else return false;}if (root->_col == Blank) Num++;if (root->_col == Red && root->_parent->_col == Red) return false;return Check(root->_left, Num, BlankNum) &&Check(root->_right, Num, BlankNum);}bool _IsBalance(Node* root){if (root->_col == Red) return false;Node* cur = root;int BlankNum = 0;while (cur){if (cur->_col == Blank) BlankNum++;cur = cur->_left;}//检查每条路径的黑色节点是否相同return Check(root, 0, BlankNum);}void _InOrder(Node* root){if (root == nullptr) return;_InOrder(root->_left);//cout << root->_kv.first << ":"<<root->_kv.second<<endl;cout << Return(root->_kv) << endl;_InOrder(root->_right);}};
}// myset.h
#pragma oncenamespace bit
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& val){return val;}};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& val){return _t.Insert(val);}private:RBTree<K, const K, SetKeyOfT> _t;};void PrintConstIterator(const set<int>& m){set<int>::const_iterator it = m.begin();while (it!=m.end()){cout << *it << ' ';++it;}cout << endl;}void test_set(){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);cout << "ConstIterator:" << endl;PrintConstIterator(s);cout << "Iterator:" << endl;set<int>::iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;set<int> copy = s;for (auto e : copy){cout << e << " ";}cout << endl;}
}//mymap.h
#pragma oncenamespace bit
{template<class K,class V>class map{//内部类,返回在红黑树中要进行使用的值struct MapKeyOfT{const K& operator()(const pair<K, V>& val){return val.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();}iterator find(const K& val)//模板的第一个类型这里要用到{return _t.find(val);}pair<iterator,bool> insert(const pair<K, V>& val){return _t.Insert(val);}//不同情况[]的使用含义不同V& operator[](const K& val){pair<iterator, bool> ret = _t.Insert(make_pair(val, V()));return ret.first->second;//用迭代器的方式返回红黑树储存的第二个值}private://第一个值不能进行修改RBTree<K, pair<const K,V>, MapKeyOfT> _t;};//测试代码void PrintMap(const map<int, int>& m){map<int, int>::const_iterator it = m.begin();while (it != m.end()){cout << it->first << ":" << it->second << endl;++it;}cout << endl;}void test_map(){map<int, int> m;m.insert({1,1});m.insert({ 3,3 });m.insert({ 9,9 });m.insert({ 5,5 });cout << "ConstIterator:" << endl;PrintMap(m);cout << "Iterator"<<endl;map<int, int> n = m;map<int, int>::iterator it = n.begin();while (it != m.end()){//it->first += 'x';//cout << it.operator->()->first << ":" << it->second << endl;cout << it->first << ":" << it->second << endl;++it;}cout << endl;}void test_map1(){string arr[] = { "苹果", "西瓜","西瓜" ,"草莓","草莓","草莓"};map<string, int> countMap;for (auto& e : arr){countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}}
}//test.cpp
#include"RBTree.h"#include"mymap.h"
#include"myset.h"int main()
{bit::test_set();//bit::test_map1();return 0;
}

以上便是学习map和set的知识总结,有错误欢迎在评论区指出,Thanks♪(・ω・)ノ

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

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

相关文章

紫光展锐突破创新终端品类,搭载展锐芯的全球首款二合一5G云电脑正式发布

近日&#xff0c;搭载紫光展锐5G芯片T760的中兴云电脑逍遥系列正式发布&#xff0c;亮点&#xff1a; 全球首款二合一5G云电脑&#xff0c;支持本地/云端双模式&#xff0c;一键切换&#xff0c;用户可同时享有Android平板和Windows云电脑两种形态&#xff1b;支持5G蜂窝网络&…

C++相关概念和易错语法(13)(string的模拟实现)

string由于存在字符串和单字符的概念&#xff0c;使得它的一些接口&#xff0c;实现要比vector多一些。本质上来看string的实现是在顺序表的基础上加入串相关的操作。下面我会分享如何模拟实现string&#xff0c;这可以进一步提高我们对string的熟练程度。 1.构造函数、拷贝构…

附代码:策略常用-正余弦优化算法

正余弦优化算法作为群智能优化算法的一种, 正弦余弦算法 (sine cosine algorithm, SCA) 是 2016 年由 Mirjalili 提出的一种新型仿自然优化算法, 通过创建多个随机候选解, 利用正余弦函数的数学性质来平衡算法在搜系过程中的全局探索和局部开发能力。该算法具有结构简单、参数少…

docker三种自定义网络(虚拟网络) overlay实现原理

docker提供了三种自定义网络驱动&#xff1a;bridge、overlay、macvlan。 bridge驱动类似默认的bridge网络模式。 overlay和macvlan是用于创建跨主机网络。 支持自定义网段、网关&#xff0c;docker network create --subnet 172.77.0.0/24 --gateway 172.77.0.1 my_n…

PPT大珩助手新功能-生成迷宫

大珩助手是一款功能丰富的办公软件插件&#xff0c;它主要分为两个版本&#xff1a;PPT大珩助手和Word大珩助手。这两个版本都旨在提高用户在处理演示文稿和文档时的效率。 PPT大珩助手 这是一款专门为Microsoft PowerPoint设计的插件。它提供了多种功能&#xff0c;例如素材…

盲人社区生活支持体系:织就一张温暖的网

在当今社会&#xff0c;构建一个全面、包容的盲人社区生活支持体系成为了推动社会进步、保障残障人士权益的重要议题。随着科技的不断革新&#xff0c;一款名为“蝙蝠避障”的辅助软件走进了盲人的日常生活&#xff0c;它如同一位无形的向导&#xff0c;通过实时避障与拍照识别…

element DatePicker 日期选择器设置禁用未来日期,时间范围为60天

需要用到 DatePicker 里面的 picker-options 方法 disabledDate onPick方法 <el-date-pickerv-model"form.xxxx"type"daterange"value-format"yyyy-MM-dd":clearable"false":picker-options"pickerOptions"start-placeho…

运行Android项目时,提示错误: 程序包javax.annotation.processing不存在

今天在运行项目时提示错误: 错误: 程序包javax.annotation.processing不存在 import javax.annotation.processing.Generated; 最后是修改了Android Studio的JDK的路径修改为你安装的JDK路径&#xff0c;完成的修复&#xff1a;

5.23 Linux中超时检测方式+模拟面试

1.IO多路复用的原理&#xff1f; IO多路复用使得一个或少量线程资源处理多个连接的IO事件的技术。对于要处理的多个阻塞的IO操作&#xff0c;建立集合并存储它们的文件描述符&#xff0c;利用单个阻塞函数去监控集合中文件描述符事件到达的情况&#xff0c;&#xff08;如果到…

cPanel中如何移除之前添加的域名

我这边想要移除我之前绑定到主机的域名&#xff0c;但是不知道如何在主机上面进行移除&#xff0c;由于我使用的Hostease的Linux虚拟主机产品默认带普通用户权限的cPanel面板&#xff0c;但是不知道如何在cPanel上操作移除域名&#xff0c;因为也是对于Hostease主机产品不是很了…

Linux磁盘高级操作

RAID RAID存储系统是一种数据存储虚拟化技术&#xff0c;它将多个物理磁盘驱动器组合成一个或多个逻辑单元&#xff0c;以提供数据冗余和/或提高性能。 1. RAID 0 无奇偶校验与冗余&#xff08;磁盘容错&#xff09;的条带存储&#xff08;带区卷/条带卷&#xff09; 由两块…

基于windows通过kind部署轻量级便携式k8s集群

感谢老师的视频教程&#xff1a; 基于windows通过kind部署轻量级便携式k8s集群 wsl windows下的linux wsl --set-default-version 2 wsl --help wsl --list --online wsl --install -d Ubuntu wsl -l -v &#xff08;看看版本是不是2&#xff0c;否则docker那边识别不到&…

从零开始:在线教育系统源码与知识付费小程序开发指南

开发一个功能完善的在线教育系统和知识付费小程序并不是一件简单的事情。今天&#xff0c;小编将从零开始&#xff0c;详细介绍如何开发在线教育系统和知识付费小程序。 一、需求分析与系统设计 在开始编写代码之前&#xff0c;首先需要进行需求分析和系统设计。这一步非常关键…

汽车摄像头智能画质增强解决方案,高品质车载视觉系统

在数字化与智能化浪潮的推动下&#xff0c;汽车行业正经历着一场前所未有的技术革命。其中&#xff0c;车载摄像头作为智能驾驶与安防监控的核心部件&#xff0c;其画质的高低直接关系到行车安全与驾驶体验。美摄科技&#xff0c;作为行业领先的智能图像技术解决方案提供商&…

机器学习圣经PRML作者Bishop推出重磅教材

图1 书籍《Pattern Recognition and Machine Learning》 只要学人工智能的人&#xff0c;必然学机器学习。 只要学机器学习的人&#xff0c;必然看PRML。 PRML为何物&#xff1f; PRML全名《Pattern Recognition and Machine Learning》&#xff0c;一部机器学习领域的内功…

数据库系列之MySQL数据库中内存使用分析

在实际系统环境中&#xff0c;MySQL实例的内存使用随着业务的增长缓慢增长&#xff0c;有些时候并没有及时的释放。本文简要介绍下MySQL数据库中和内存相关的配置&#xff0c;以及分析内存的实际使用情况&#xff0c;以进行应急和调优处理。 1、MySQL内存结构 在MySQL中内存的…

MySQL索引和视图

MySQL索引和视图是关系型数据库MySQL中的两个重要概念。索引用于优化数据库的查询性能&#xff0c;而视图用于提供一个逻辑上的表结构&#xff0c;方便用户查询和操作数据。 索引是一种数据结构&#xff0c;可以加速对数据库表中的数据进行查询的速度。通过创建索引&#xff0…

激光雷达SLAM算法综述

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

BEVFusion的相机工作流中,图像编码之后FPN+ADP网络的作用

在BEVFusion的相机工作流中&#xff0c;图像编码之后会经过一个FPNADP的网络,那么这个结构的作用是什么呢 FPN大家都很熟悉&#xff0c;就是特征金字塔。但是这里还是贴一些来自GPT的废话 在Bird’s Eye View (BEV) 算法中使用的特征金字塔网络&#xff08;FPN, Feature Pyrami…

视频监控管理平台LntonCVS监控视频汇聚融合云平台主要功能应用场景介绍

随着网络技术的不断发展和万物互联时代的到来&#xff0c;视频融合在一些系统集成项目及综合管理应用中变得日益重要。本文以LntonCVS视频融合云平台为案例&#xff0c;探讨视频融合的对象及其应用场景。 1. 视频监控设备 视频监控摄像设备是各种视频应用项目的基础部分。在视…