【C++】set 和 map 学习及使用

🔥博客主页:   小羊失眠啦.
🎥系列专栏《C语言》 《数据结构》 《C++》 《Linux》
❤️感谢大家点赞👍收藏⭐评论✍️


前言

setmapSTL 中的容器之一,不同于普通容器,它俩的查找速度极快,常用来存储各种经常被检索的数据,因为这俩容器的底层是平衡二叉搜索树中的红黑树。除此之外,还可以借助其特殊的性质,解决部分难题


一、预备知识

在正式学习 setmap 之前,首先要有一些预备知识,否则后面可能看不懂相关操作

1.1 关联式容器

在以往的 STL 容器学习中,我们接触到的都是 序列式容器,比如 stringvectorlistdeque 等,序列式容器的特点就是 底层为线性序列的数据结构,就比如 list,其中的节点是 线性存储 的,一个节点存储一个元素,其中存储的元素都可序,但未必有序
关联式容器 则比较特殊,其中存储的是 <key, value>键值对,这就意味着可以按照 键值大小 key 以某种特定的规则放置于适当的位置,关联式容器 没有首尾的概念,因此没有头插尾插等相关操作,本文中学习的 setmap 就属于 关联式容器
image.png
注意: **stackqueue 等适配器也属于序列式容器,因为他们的底层是 deque 等容器**

1.2 键值对

键值对是 一种用来表示具有一一对应关系的结构,该结构中一般只包含两个成员变量:keyvalue,前者表示 键值,后者表示 实值
关联式容器的实现离不开键值对
因此在标准库中,专门提供了这种结构 pair**定义如下

//SGI 版 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) {}#ifdef __STL_MEMBER_TEMPLATEStemplate <class U1, class U2>pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
#endif
};

pair 中的 first 表示 键值second 则表示 实值,在给 关联式容器 中插入数据时,可以构建 pair 对象
比如下面就构建了一个 键值 keystring,实值 valueint 的匿名 键值对 pair 对象

pair<string, int>("hehe", 123);

可以将此匿名对象传入 关联式容器 中,当然这样写未免过于麻烦了,于是库中设计了一个函数模板 make_pair,可以根据传入的参数,去调用 pair 构建对象并返回

make_pair("hehe", 123);	//构建出的匿名对象与上面的一致

make_pair 的定义如下所示:

template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{return ( pair<T1,T2>(x,y) );
}

该函数实际会被编译器优化为 内联函数,因此不会造成过多消耗,可以放心使用

1.3 树型结构的关联式容器

所以在 C++ 标准中,共提供了四种 树型结构的关联式容器

  • set
  • multiset
  • map
  • multimap

关于 哈希结构的关联式容器 将在 哈希表 中学习
树型结构与哈希结构的关联式容器功能都是一模一样的,不过 哈希结构查找比树型结构快得多 -> O(1)

注:

  • STL 中选择的树型结构为 红黑树 RB-Tree
  • 树型结构中的元素 中序遍历 后有序,而哈希结构中的元素无序

二、set

2.1 什么是 set?

set 其实就是之前在 二叉搜索树key 的模型
image.png
set 只包含 实值 value,或者说它的 实值就是键值,键值就是实值

在这里插入图片描述

其中的 T 就是 set 的实值(键值),参数2 Compare 为存储依据,默认为升序,即符合 二叉搜索树 中序遍历的结果:升序,参数3 Alloc 是空间配置器,现在不必深究

作为 STL 中的容器,set 当然少不了迭代器,树型关联式容器迭代器的遍历结果为有序,所以迭代器遍历的本质是 中序遍历,同时 set 的迭代器还是一个 双向迭代器,支持 ++-- 操作

在这里插入图片描述

下面来看看 set 的相关操作

2.2 set 的使用

下面来看看 set 的相关操作:

在这里插入图片描述

可以直接创建一个空 set 使用,也可以根据迭代器区间创建 set

注意: 创建时需要指定实值的类型

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 8, 5, 6, 7, 3, 1, 2, 3 };set<int> s1;       //创建一个空的 setset<int> s2(arr.begin(), arr.end());  //创建包含数据的 setcout << "s1: ";for (auto e : s1){cout << e << " ";}cout << endl;cout << "s2: ";for (auto e : s2){cout << e << " ";}cout << endl;return 0;
}

在这里插入图片描述

就像 二叉搜索树 一样,set 是不支持数据冗余的,如果出现冗余的数据插入时,会失败,如果想存储冗余的数据,可以使用 multiset

set 中的常用功能

功能用途
迭代器遍历容器
empty判断容器是否为空
size当前容器中的元素数
max_size容器的最大容量
insert元素插入,根据特定条件插入至合适位置
erase删除指定元素
swap交换两个容器
clear清空容器中的所有元素
find查找实值是否存在并返回迭代器位置
count统计容器中指定键值的数量

下面这段代码演示了上述功能的实际效果

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 7, 3, 6, 9, 3, 1, 6, 2 };set<int> s1(arr.begin(), arr.end());//迭代器遍历cout << "迭代器遍历结果:";set<int>::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;cout << "-------------------------------------------" << endl;cout << "empty: " << s1.empty() << endl;cout << "size: " << s1.size() << endl;cout << "max_size: " << s1.max_size() << endl;//插入元素cout << "-------------------------------------------" << endl;cout << "insert(5): ";s1.insert(5);for (auto e : s1)cout << e << " ";cout << endl;//删除元素cout << "-------------------------------------------" << endl;cout << "erase(6): ";s1.erase(6);for (auto e : s1)cout << e << " ";cout << endl;//交换、查找、清理cout << "-------------------------------------------" << endl;set<int> s2(arr.begin() + 5, arr.end());s1.swap(s2);cout << "s1: ";for (auto e : s1)cout << e << " ";cout << endl;cout << "s2: ";for (auto e : s2)cout << e << " ";cout << endl;cout << "s1.find(9): ";cout << (s1.find(9) != s1.end()) << endl;cout << "s2.clear(): " << endl;s2.clear();cout << "s1: ";for (auto e : s1)cout << e << " ";cout << endl;cout << "s2: ";for (auto e : s2)cout << e << " ";cout << endl;return 0;
}

在这里插入图片描述

至于 count 也可以用来查找元素是否存在,对于 set 来说,键值 key 就是 实值 value,并且因为不允许冗余,所以只有一个 键值count 统计 键值 数量不就相当于 查找 吗?

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 7, 3, 6, 9, 3, 1, 6, 2 };set<int> s1(arr.begin(), arr.end());for (int i = 0; i < 10; i++){if (s1.count(i))cout << i << " 在 set 中" << endl;elsecout << i << " 不在 set 中" << endl;}return 0;
}

在这里插入图片描述

可以通过改变 set 模板参数2的方式,改变其中的顺序为 降序

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 7, 3, 6, 9, 3, 1, 6, 2 };set<int, greater<int>> s1(arr.begin(), arr.end());for (auto e : s1)cout << e << " ";return 0;
}

在这里插入图片描述

注意: 键值 key 是不允许改变的,如果改变了,会破坏二叉搜索树的原则,因此即使是 set 中的普通迭代器,本质上也是 const 迭代器,非常神奇

在这里插入图片描述

2.3 set的特点

set 具有以下特点:

在这里插入图片描述

set 还有一个亲兄弟:multiset,它允许数据冗余,即数据插入一定是成功的

2.4 multiset

multisetset 的另一个版本,对于 multiset 来说,插入冗余数据时,并不会失败

在这里插入图片描述

除此之外,multisetset 的操作没什么区别,一模一样

这里就不再赘述,而是单独演示一下允许数据冗余的效果

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 3, 5, 3, 4, 5, 9, 2, 3 };multiset<int> ms1(arr.begin(), arr.end());for (auto e : ms1)cout << e << " ";cout << endl;return 0;
}

在这里插入图片描述

值得一提的是,当在 multiset 中查找冗余的数据时,返回的是 中序遍历中,第一次出现的元素

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 3, 5, 3, 4, 5, 9, 2, 3 };multiset<int> ms1(arr.begin(), arr.end());auto it = ms1.begin();while (it != ms1.end()){cout << *it << " : " << &*it << endl;++it;}cout << "--------------------------" << endl;cout << "ms1.find(3): " << &*ms1.find(3) << endl;return 0;
}

在这里插入图片描述

所以,multiset 才是真正的排序,set 则是去重 + 排序

统计 键值countmultiset 中可以发挥真正效果

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 3, 5, 3, 4, 5, 9, 2, 3 };multiset<int> ms1(arr.begin(), arr.end());for (int i = 0; i < 10; i++)cout << i << "在 multiset 中的数量:" << ms1.count(i) << endl;return 0;
}

在这里插入图片描述

在实际中,multiset 用的比较少,重点掌握 set 即可


三、map

3.1 什么是 map?

map二叉搜索树 改造后的 key / value 模型,是一个真正意义上的 键值对,应用场景如下:

在这里插入图片描述

map 的定义如下

在这里插入图片描述

其中包含两个模板参数:

  1. Key 就是键值对中的 键值
  2. T 则是键值对中的 实值

map 中会用到前面提到过的 pair 结构,其中 first 表示键值,second 表示实值

map 也有迭代器,也是 双向迭代器map 也有迭代器,也是 双向迭代器

在这里插入图片描述

3.2 map 的使用

构造 map 有以下几种方法

在这里插入图片描述

#include <iostream>
#include <vector>
#include <map>
using namespace std;int main()
{vector<pair<string, int>> arr = { make_pair("G", 71), make_pair("A", 65), make_pair("F", 70) };map<string, int> m1;map<string, int> m2(arr.begin(), arr.end());cout << "m1: " << endl;for (auto e : m1)cout << e.first << " : " << e.second << endl;cout << "--------------------------------" << endl;cout << "m2: " << endl;for (auto e : m2)cout << e.first << " : " << e.second << endl;return 0;
}

在这里插入图片描述

注意: 在访问 map 中的 键值 和 实值 时,需要通过 pair 对象指定访问,比如 e.first

map 中的常用功能

功能用途
迭代器遍历容器
empty判断容器是否为空
size当前容器中的元素数
max_size容器的最大容量
operator[]按照键值,访问实值,如果没有,则新插入
insert元素插入,根据特定条件插入至合适位置
erase删除指定元素
swap交换两个容器
clear清空容器中的所有元素
find查找实值是否存在并返回迭代器位置
count统计容器中指定键值的数量统计容器中指定键值的数量统计容器中指定键值的数量

除了新增了一个 operator[] 以及部分函数返回值不一样外,与 set 没啥区别

#include <iostream>
#include <vector>
#include <map>
using namespace std;int main()
{vector<pair<string, int>> arr{ make_pair("z", 122), make_pair("a", 97), make_pair("K", 75), make_pair("h", 104), make_pair("B", 66) };map<string, int> m1(arr.begin(), arr.end());cout << "迭代器遍历结果:";map<string, int>::iterator it = m1.begin();while (it != m1.end()){cout << "<" << it->first << ":" << it->second << "> ";++it;}cout << endl;//判空、求大小、解引用cout << "------------------------------------------------" << endl;cout << "empty(): " << m1.empty() << endl;cout << "size(): " << m1.size() << endl;cout << "max_size(): " << m1.max_size() << endl;cout << "m1[""a""]: " << m1["a"] << endl;//插入元素cout << "------------------------------------------------" << endl;cout << "insert(""a"", 5): ";m1.insert(make_pair("a", 5));for (auto e : m1)cout << "<" << e.first << ":" << e.second << "> ";cout << endl;//删除元素cout << "------------------------------------------------" << endl;cout << "erase(""a""): ";m1.erase("a");for (auto e : m1)cout << "<" << e.first << ":" << e.second << "> ";cout << endl;//交换、查找、清理cout << "------------------------------------------------" << endl;map<string, int> m2(arr.begin() + 2, arr.end());m1.swap(m2);cout << "m1.swap(m2)" << endl;cout << "m1: ";for (auto e : m1)cout << "<" << e.first << ":" << e.second << "> ";cout << endl;cout << "m2: ";for (auto e : m2)cout << "<" << e.first << ":" << e.second << "> ";cout << endl;cout << "m1.find(""B""): ";cout << (m1.find("B") != m1.end()) << endl;cout << "m2.clear()" << endl;m2.clear();cout << "m1: ";for (auto e : m1)cout << "<" << e.first << ":" << e.second << "> ";cout << endl;cout << "m2: " << endl;for (auto e : m2)cout << "<" << e.first << ":" << e.second << ">";cout << endl;return 0;
}

在这里插入图片描述

同样的,map 不允许数据冗余,如果想插入重复的数据,可以使用 multimap

map 插入的返回值比 set 略微复杂,因为 既要表示是否成功,也要返回插入成功的迭代器,所以返回值是一个 pair

在这里插入图片描述

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;int main()
{map<string, int> m1;auto ret = m1.insert(make_pair("a", 97));cout << "<" << ret.first->first << ":" << ret.first->second << ">" << " : " << ret.second << endl;ret = m1.insert(make_pair("a", 100));cout << "<" << ret.first->first << ":" << ret.first->second << ">" << " : " << ret.second << endl;return 0;
}

在这里插入图片描述

至于 findcountset 中的一样,可以用来判断元素是否存在,不过 find 返回的是 迭代器count 返回的则是 键值数

map 是支持修改 实值 value 的,因此 可以根据普通迭代器修改 实值

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;int main()
{map<string, int> m1;m1.insert(make_pair("a", 97));auto it = m1.find("a");cout << "<" << it->first << ":" << it->second << ">" << endl;it->second = 666;cout << "<" << it->first << ":" << it->second << ">" << endl;return 0;
}

在这里插入图片描述

使用 map 来实现水果统计的代码

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;int main()
{vector<string> word = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };map<string, int> table;for (auto& e : word){if (!table.count(e))table.insert(make_pair(e, 1));elsetable.find(e)->second++;}for (auto e : table)cout << "<" << e.first << ":" << e.second << ">" << endl;return 0;
}

在这里插入图片描述

可以实现统计,但这种写法太麻烦了,实际不会这么写,可以使用 operator[] 实现更高级的写法

3.3 map 中的 operator[]

operator[] 返回的是当前 键值 对应的 实值,如果当前 键值 不存在,则会插入新的 键值对

借助此特性,可把代码优化为

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;int main()
{vector<string> word = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };map<string, int> table;for (auto& e : word){table[e]++;}for (auto e : table)cout << "<" << e.first << ":" << e.second << ">" << endl;return 0;
}

在这里插入图片描述

显然,map 中的 operator[] 是一个非常强大的功能

在这里插入图片描述

operator[] 的返回值为 mapped_type,即 实值 value 的引用,参数 key_type键值 `key

重点在于 operator[] 的实现:如何凭借 键值 返回对应的 实值,并且做到新键值对的插入

(*((this->insert(make_pair(k, mapped_type()))).first)).second

在这里插入图片描述

总的来说,operator[] 返回时需要经历以下步骤:

  • 插入一个新的键值对 this->insert( make_pair(k, mapped_type()) )
  • 获取 insert 返回值中的 键值 返回值.first 即迭代器 iterator
  • 最后通过迭代器获取 实值 (*iterator).second

只需三步,即可获取 实值

其实上面那样定义还复杂了,可以优化为下面这个样子

( (this->insert( make_pair(k, mapped_type()) )).first )->second

所以一个 operator[] 兼顾了这几种功能:插入、修改、插入+修改、查找

map 中最强大的功能

3.4 map 的特点

归纳总结后,map 的特点如下图所示

在这里插入图片描述

注意: 无论是查找、插入、删除还是排序,都只看 键值 key,至于 实值 value 的内容是什么,无所谓,它只不过是 键值 额外携带的一个信息包而已

multimap 允许出现键值冗余

3.5 multimap

在这里插入图片描述

multimap 中允许出现多个 重复的键值,这就意味着 operator[] 无法确认调用者的意图 -> 不知道要返回哪个 键值 对应的 实质

所以 multimap 中没有提供 operator[]

在这里插入图片描述

在这里插入图片描述

除了 允许键值冗余没有 operator[] 这个两个特点外,multimapmap 在操作上没有区别

当然,查找 find 时,返回的是中序遍历中第一次出现元素的迭代器;计数 count 返回的则是当前 键值 的数量

multiset 一样,multimap 用的也比较少,重点掌握 setmap 即可


四、相关试题实战

学会使用 setmap 后,可以将其用于实战,比如在下面这两个题中,这两个容器可以让我们事半功倍

4.1 前K个高频单词

692. 前K个高频单词

在这里插入图片描述

题目分析:题目很短,就是在一个字符串数组中,找出前 k 个出现频率最高的单词

注意: 如果出现次数相同,则按字典序排序

这道题有很多种解法

解法一:map + 快排

利用 map 建立 <string, int> 的映射关系,在按照字典序排序的同时统计出每个单词的出现频率,再通过快排依照数量进行二次排序,选择前 k 个高频单词即可

因为基础版快排 不稳定,可能会导致频率相同的单词顺序出问题,即违背题目要求:如果出现频率相同,则按字典序排序

所以这里需要使用 稳定版快排 stable_sort,如果频率相同,保持原有顺序

class Solution {
public:struct Compare{bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const{return kv1.second > kv2.second;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> m;for (auto e : words)m[e]++;vector<pair<string, int>> vm(m.begin(), m.end());stable_sort(vm.begin(), vm.end(), Compare());vector<string> vs;for (int i = 0; i < k; i++)vs.push_back(vm[i].first);return vs;}
};

注意: 此时使用快排进行排序时,单个元素是 pair,需要自己写出仿函数进行排序,仿函数十分强大

难道基础版快排无法完成任务吗?

当然可以,只需要将 仿函数进行设计即可:优先按照出现频率排序,如果频率相同,则按照字典序排序即可

具体代码如下(用了一点 C++11 中的知识)

class Solution {
public:vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> m;for (auto e : words)m[e]++;vector<pair<string, int>> vm(m.begin(), m.end());stable_sort(vm.begin(), vm.end(), [](const pair<string, int>& kv1, const pair<string, int>& kv2)->bool{return kv1.second == kv2.second ? kv1.first < kv2.first : kv1.second > kv2.second;});vector<string> vs;for (int i = 0; i < k; i++)vs.push_back(vm[i].first);return vs;}
};

C++11 中的 lambda 表达式还是很香的

注意: 优先按照出现频率进行排序,如果频率相同时,就按字典序排序,所以写成 kv1.first < kv2.first (小的单词排在前面,就是字典序)

解法二:map + set

同样的,先使用 map 统计单词出现频率,此时已经按照字典序进行了排序,然后将 pair 看作一个 键值 存入 set 中,改变 set 中的比较逻辑(先按出现频率排序,如果相关就按照字典序排序

整体思路与 map + sort 没啥区别,不过此时是直接使用 set 进行排序,没必要借助 vector

class Solution {
public:struct Compare{bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const{return kv1.second == kv2.second ? kv1.first < kv2.first : kv1.second > kv2.second;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> m;for (auto e : words)m[e]++;set<pair<string, int>, Compare> sortSet(m.begin(), m.end());vector<string> vs;auto it = sortSet.begin();for (int i = 0; i < k; i++)vs.push_back(it++->first);return vs;}
};

解法三:map + multimap

这个解法就有点狠了,直接使用 mapmultimap 互导,完成排序

map 按照字典序排序,并统计出频率
multimap map 的基础上,按照 频率 排序

注意: 需要使用 multimap,避免相同频率的单词丢失

class Solution {
public:vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> m;for (auto e : words)m[e]++;multimap<int, string, greater<int>> mm;for (auto& e : m)mm.insert(make_pair(e.second, e.first));vector<string> vs;auto it = mm.begin();for (int i = 0; i < k; i++)vs.push_back(it++->second);return vs;}
};

这种写法十分巧妙,代码也很简洁,完美体现了 mapmultimap 的价值

关于这道题还有其他解法,比如 利用优先级队列解决 Tok-K,感兴趣的同学可以自己下去研究,这里就不再展开叙述

4.2 复杂链表的复制

LCR 154. 复杂链表的复制

在这里插入图片描述 在这里插入图片描述

题目分析:复杂链表的深度拷贝,将题目给定的链表进行复制,这个链表比较特殊,不仅指向下一个节点,还随机指向空或其他节点

之前的解法是在两个节点新增节点,然后更改链接关系,比较麻烦,现在可以借助 map 建立映射关系,直接照着原链表更改链接关系即可

在这里插入图片描述

/*
// Definition for a Node.
class Node {
public:int val;Node* next;Node* random;Node(int _val) {val = _val;next = NULL;random = NULL;}
};
*/
class Solution {
public:Node* copyRandomList(Node* head) {map<Node*, Node*> copyNodeMap;Node* cur = head;Node* copyHead = nullptr;Node* copyTail = nullptr;while (cur){Node* copy = new Node(cur->val);copyNodeMap[cur] = copy;if (copyHead == nullptr)copyHead = copyTail = copy;else{copyTail->next = copy;copyTail = copyTail->next;}cur = cur->next;}cur = head;while (cur){copyNodeMap[cur]->random = copyNodeMap[cur->random];cur = cur->next;}return copyHead;}
};

4.3 如何查找交集

交集,指两个数组中相同的元素所构成的集合

求交集的步骤如下:

  1. 先将两个数组 排序 + 去重
  2. 遍历两个数组
  3. 如果不相等,小的 ++
  4. 相等就是交集,记录下来
  5. 其中一方走完,所有交集就查找完了

排序 + 去重,这就不就是 set 吗?

class Solution {
public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {set<int> s1(nums1.begin(), nums1.end());set<int> s2(nums2.begin(), nums2.end());vector<int> v;auto it1 = s1.begin();auto it2 = s2.begin();while (it1 != s1.end() && it2 != s2.end()){if (*it1 < *it2)++it1;else if (*it1 > *it2)++it2;else{v.push_back(*it1);++it1, ++it2;}}return v;}
};

4.4如何查找差集?

至于差集的查找,思路和交集差不多

求差集的步骤如下:

  1. 先将两个数组 排序 + 去重
  2. 遍历两个数组
  3. 如果相等,同时 ++
  4. 不相等,小的一方记录后,再 ++
  5. 其中一方走完,再遍历另一方,此时其中的所有元素都是差集

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

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

相关文章

开发一款相亲交友小程序

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 前端开发:vue.js 可选语言&#xff1a;pythonjavanode.jsphp均支持 运行软件…

第四届上海理工大学程序设计全国挑战赛 J.上学 题解 DFS 容斥

上学 题目描述 usst 小学里有 n 名学生&#xff0c;他们分别居住在 n 个地点&#xff0c;第 i 名学生居住在第 i 个地点&#xff0c;这些地点由 n−1 条双向道路连接&#xff0c;保证任意两个地点之间可以通过若干条双向道路抵达。学校则位于另外的第 0 个地点&#xff0c;第…

【JAVA进阶篇教学】第十三篇:Java中volatile关键字讲解

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第十三篇&#xff1a;volatile关键字讲解。 在 Java 中&#xff0c;volatile关键字是一种轻量级的同步机制&#xff0c;用于确保变量的可见性和禁止指令重排序。本文将详细解释volatile关键字的工作原理、可见性保证以及…

Web安全:SQL注入之布尔盲注原理+步骤+实战操作

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

ansible------inventory 主机清单

目录 inventory 中的变量 2&#xff09;组变量[webservers:vars] #表示为 webservers 组内所有主机定义变量&#xff0c;所有组内成 员都有效 ansible_userrootansible_passwordabc1234 3&#xff09; [all:vars…

实现字符串复制(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int i 0;char a[100], b[100];//获取字符串&#xff1b;printf("请为数组a输入字符串…

回溯算法精讲

原理 回溯&#xff0c;就和深度优先遍历&#xff08;DFS&#xff09;类似&#xff0c;属于先一层到底直至到终点&#xff0c;如果这条路径不对&#xff0c;则回退一下&#xff0c;再继续往下搜索。 抽象地说&#xff0c;解决一个回溯问题&#xff0c;实际上就是遍历一棵决策树…

未来已来:Spring Cloud引领微服务新纪元

文章目录 1. 引言1.1 微服务架构的兴起与挑战1.2 引入Spring Cloud作为微服务解决方案的重要性 2. 背景介绍2.1 微服务架构概述2.2 Spring Cloud的定位2.3 Spring Cloud特性概览 3. Spring Cloud核心组件3.1 Eureka - 服务发现3.2 Hystrix - 断路器3.3 Ribbon - 客户端负载均衡…

Python vs MATLAB:选择深度学习的首选编程语言

Python vs MATLAB&#xff1a;选择深度学习的首选编程语言 在深度学习领域&#xff0c;编程语言的选择对于初学者的学习路径和未来的职业发展至关重要。目前&#xff0c;Python和MATLAB都是进行科学计算和数据分析的流行工具&#xff0c;但它们在深度学习社区中的应用和受欢迎…

构建教育新未来:智慧校园平台的深度解读与全景呈现

引言 在全球数字化转型的大潮中&#xff0c;智慧校园平台作为教育信息化的重要载体&#xff0c;正以前所未有的姿态颠覆传统的教育模式&#xff0c;引领教育行业步入一个崭新的时代。这个融合了大数据、人工智能、云计算、物联网等一系列前沿科技的平台&#xff0c;以其强大的功…

C++笔记:类和对象(一)

一.类的引入 1.类(class)是C中的一种特殊的数据结构&#xff0c;它封装了数据和操作这些数据的函数。 2.类中的数据被称为成员变量&#xff0c;而函数则被称为成员函数。 3.类可以被看作是一种模板&#xff0c;用于创建具有相同属性和行为的多个对象。 在C语言中结构体是一种复…

数字化运维体系:重塑IT运维的未来面貌

随着信息技术的飞速发展&#xff0c;传统运维模式已难以满足现代企业日益增长的复杂需求。在此背景下数字化运维体系应运而生&#xff0c;以其独特的视角和方法论为IT运维领域带来了革新性的变革。本文将深入探讨数字化运维体系的定义、核心要素&#xff0c;并阐述如何通过这一…

rust开发web服务器框架,github排名对比

Rocket Star最多的框架 github仓库地址&#xff1a;GitHub - rwf2/Rocket: A web framework for Rust. Rocket 是一个针对 Rust 的异步 Web 框架&#xff0c;重点关注可用性、安全性、可扩展性和速度。 Axum 异步运行时 githuh仓库地址&#xff1a;GitHub - tokio-rs/axum: …

Redis—图文详解高可用原因

本文不会讲解Redis的用途&#xff0c;关于用途会发另一片文章讲解&#xff0c;本文主要讲的是高可用的原理。 Redis高可用主要有以下三个原因&#xff1a;主从模式(上一篇讲Kafka的文章里有涉及到)&#xff0c;哨兵模式&#xff0c;Redis-Cluster(Redis集群)。 什么是主从模式…

【2024高校网络安全管理运维赛】巨细记录!

2024高校网络安全管理运维赛 文章目录 2024高校网络安全管理运维赛MISC签到考点&#xff1a;动态图片分帧提取 easyshell考点&#xff1a;流量分析 冰蝎3.0 Webphpsql考点&#xff1a;sql万能钥匙 fileit考点&#xff1a;xml注入 外带 Cryptosecretbit考点&#xff1a;代码阅读…

数据结构与算法-时间复杂度

这篇文章会讲到时间频度&#xff0c;时间复杂度&#xff0c;一些排序算法的平均时间复杂度与最坏时间复杂度 目录 1.度量一个程序&#xff08;算法&#xff09;的两种方法 1&#xff09;事后统计法 2&#xff09;事前估算法 2.时间频度 1.基本介绍 算法1&#xff1a; 算…

C语言 | Leetcode C语言题解之第86题分隔链表

题目&#xff1a; 题解&#xff1a; struct ListNode* partition(struct ListNode* head, int x) {struct ListNode* small malloc(sizeof(struct ListNode));struct ListNode* smallHead small;struct ListNode* large malloc(sizeof(struct ListNode));struct ListNode* …

SpringCloud微服务之Eureka、Ribbon、Nacos详解

SpringCloud微服务之Eureka、Ribbon、Nacos详解 1、认识微服务1.1、单体架构1.2、分布式架构1.3、微服务1.4、SpringCloud 2、服务拆分与远程调用2.1、服务拆分的原则2.2、服务拆分示例2.2、提供者与消费者 3、Eureka注册中心3.1、Eureka的结构和作用3.2、搭建eureka-server3.2…

使用IIS部署Vue项目

前提 使用IIS部署Vue项目&#xff0c;后端必须跨域&#xff0c;不要在Vue中用proxy跨域&#xff0c;那个只在dev环境中有用&#xff01; IIS安装&#xff0c;不用全部打勾&#xff0c;有些他默认就是方块 ■ 选择性安装的&#xff0c;就维持原样就可以。 添加网站配置 右键…

4.1 编写程序,从键盘接收一个小写字母,然后找出他的前导字符和后续字符,再按顺序显示这三个字符

方法一&#xff1a; 运行效果&#xff1a; 输入B&#xff0c;输出显示ABC&#xff1b;输入A&#xff0c;输出显示AB 思路&#xff1a; 1、通过键盘输入接收一个字母。 2、将输入的字母减去1&#xff0c;得到前导字符&#xff0c;然后输出。 3、将输入的字母加上1&#xff0c;得…