当前位置: 首页 > news >正文

【C++ map和set】数据的吟游诗:Map与Set的双城记

在这里插入图片描述

公主请阅

  • set
    • 1.序列式容器和关联式容器
    • 2.set的介绍
    • 3.set的构造和迭代器部分set可以进行去重操作的,在去重的同时可以对插入进来的数字进行排序的操作
    • 4.set的增删查
      • insert
      • erase
      • find
      • upper_bound和 lower_bound
    • 5.multiset和set的差异
    • 6相关题目
      • 349.两个数组的交集
      • 142.环形链表 II
  • Map
    • map的概念
    • pair类型介绍
    • map的构造
    • map的增删查
      • `find` 函数的行为
        • 1. 如果键 `key` 存在:
      • 举例说明
        • 输出结果:
      • 总结:
    • map的构造遍历以及增删查使用详例
    • map中的operator[]的使用
    • 题目
      • 138.随机链表的复制
      • 692. 前K个高频单词
    • pair的具体使用
      • 1. **`pair` 的基本定义**
      • 2. **创建和初始化 `pair`**
        • 方法 1:使用构造函数
        • 方法 2:使用 `std::make_pair`
        • 方法 3:使用列表初始化
      • 3. **修改 `pair` 的值**
      • 4. **`pair` 的常见用法**
        • (1) 在容器中使用
          • **在 `map` 中使用**
          • **在 `vector` 中使用**
        • (2) 用于返回多个值
      • 5. **`pair` 的比较操作**
      • 6. **常见问题**
        • (1) **自定义排序 `pair`**
      • 总结

set

1.序列式容器和关联式容器

前面我们已经接触过STL中的部分容器如:string、vector、list、deque、array、forward_list等,这些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间一般没有紧密的关联关系,比如交换一下,他依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的。

关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构,两个位置有紧密的关联关系,交换一下,他的存储结构就被破坏了。顺序容器中的元素是按关键字来保存和访问的。关联式容器有map/set系列和unordered_map/unordered_set系列。本章节讲解的map和set底层是红黑树,红黑树是一颗平衡二叉搜索树。set是kev搜索场景的结构,map是key/value搜索场景的结构。


2.set的介绍

set

在这里插入图片描述

  • set的声明如下,T就是set底层关键字的类型

  • set默认要求T支持小于比较,如果不支持或者想按自己的需求走可以自行实现仿函数传给第二个模版参数

  • set底层存储数据的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第三个参数。

  • 一般情况下,我们都不需要传后两个模版参数。

  • set底层是用红黑树实现,增删查效率是O(logN),迭代器遍历是走的搜索树的中序,所以是有序的。

前面部分我们已经学习了vector/list等容器的使用,STL容器接口设计,高度相似,所以这里我们就不再一个接口一个接口的介绍,而是直接带着大家看文档,挑比较重要的接口进行介绍。


3.set的构造和迭代器部分set可以进行去重操作的,在去重的同时可以对插入进来的数字进行排序的操作

set的底层是搜索树,所以我们是不能进行修改的

// empty (1) 无参默认构造  
explicit set (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
// range (2) 迭代器区间构造
template <class InputIterator>set (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());// copy (3) 拷贝构造
set (const set& x);
// initializer list (5) initializer  列表构造
set (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());// 迭代器是一个双向迭代器
iterator   -> a bidirectional iterator to const value_type
// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器   
reverse_iterator rbegin();
reverse_iterator rend();

4.set的增删查

set的底层是搜索树,所以是不能进行修改操作的

insert

int main()
{//这个是降序的,给一个大于的仿函数//set<int,greater<int>>s;//升序排列+去重//set<int>s;set<int>s = {10,6,8,9};s.insert(5);s.insert(2);s.insert(7);s.insert(5);s.insert({ 1,2,3 });//set<int>::iterator it = s.begin();auto it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;set<string> strset = { "sort","insert","add" };//string是按照ASCII进行比较大小的for (auto& e : strset){cout << e << " ";}cout << endl;return 0;
}

erase

删除成功就返回1,没有删除成功就返回0


int main()
{set<int>s = { 4,2,7,2,8,5,9 };for (auto e : s){cout << e << " ";}cout << endl;//s.erase(s.end()--);//因为迭代器是左闭右开的,end要进行减减操作的//删除容器中的最小值s.erase(s.begin());for (auto e : s){cout << e << " ";}cout << endl;int x;cin >> x;auto pos = s.find(x);if (pos != s.end())//end是一个非法的位置,是最后一个数据的下一个位置{s.erase(pos);}else{cout << x << "不存在" << endl;}for (auto e : s){cout << e << " ";}cout << endl;//算法库的find,时间复杂度O(N)auto pos1 = find(s.begin(), s.end(), x);//set自身实现的查找O(logN)auto pos2 = s.find(x);return 0;
}

find

两种find,但是我们set里面的find的效率更加高效,初次之外,string这个容器也有find,为的是寻找子串以及其他类型的字符串

	//算法库的find,时间复杂度O(N)auto pos1 = find(s.begin(), s.end(), x);//set自身实现的查找O(logN)auto pos2 = s.find(x);

upper_bound和 lower_bound

int main()
{set<int>myset;for (int i = 1; i < 10; i++){myset.insert(i * 10);//10 20 30 40 50 60 70 80 90}for (auto e : myset){cout << e << " ";}cout << endl;//实现查找到的[itlow,itup)包含[30,60)这段区间并且进行删除操作auto itlow = myset.lower_bound(30);//返回大于30的第一个值//返回>60的值auto itup = myset.upper_bound(60);//我们可以通过这种方法找到这个区间中包含我们想找的区间//通过这个方法可以给我们一个比我们想要找的区间大一些的空间,刚好包含我们想找的区间//然后我们就可以进行删除操作了myset.erase(itlow, itup);//通过这两个迭代区间进行删除操作for (auto e : myset){cout << e << " ";}cout << endl;return 0;
}

通过两个直接将连个数据传进去,然后编译器就会找到刚好比这个区间大的一个区间

将我们要删除的区间进行删除的操作


5.multiset和set的差异

与set不同的是,multiset仅仅是进行排序,但是不进行去重操作的

如果一个数字出现多次的话,multiset中的find是只会返回第一个的,但是我们能根据这个加上迭代器将所有的这个数字打印出来

我们还能利用count算出这个数字出现的个数

miltiset中的erase可以直接将所以的这个数字删除了,一个不剩

#include<iostream>
using namespace std;                                 
#include<set>
int main()
{//与set不同的是,multiset是排序,但是不进行去重操作的multiset<int>s = { 4,5,6,11,4,5,1,1, };auto it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;int x;cin >> x;auto pos = s.find(x);while (pos != s.end() && *pos == x){cout << *pos << " ";++pos;}//find进行数据的寻找,如果有多个会返回第一个//因为我们的multiset会将数组进行排序的操作,默认从小到大的排序//那么我们这里利用find找到了第一个4,然后如果有相同的4的话那么都是跟在第一个4的后面的cout << endl;//相比set不同的是,count回返回x的实际个数cout << s.count(x) << endl;//conut如果这个数字有多个的话那么就会返回这个数字的个数的//相比set不同的是,erase给值时会删除所有的xs.erase(x);for (auto e : s){cout << e << " ";}cout << endl;return 0;
}

6相关题目

349.两个数组的交集

349.两个数组的交集
在这里插入图片描述

class Solution
{
public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2){//依次进行比较//1.小的++//2.相等的就是交集,同时++,其中一个结束就是结束了//排序+去重set<int> s1(nums1.begin(),nums1.end());//set是可以利用迭代器区间进行初始化操作的set<int> s2(nums2.begin(),nums2.end());vector<int> ret;auto it1=s1.begin();auto it2=s2.begin();while(it1!=s1.end()&&it2!=s2.end()){//谁小谁就++if(*it1<*it2){it1++;}else if(*it2<*it1){it2++;}//相等的就是交集,然后我们同时++else{ret.push_back(*it1);it1++;it2++;}}return ret;}
};

我们先利用set进行排序操作

然后创建一个vector的容器来进行结果的存储

我们这个题的原理就是我们连个数组依次进行比较的操作

小的就进行++,大的不动

如果出现了两个数字相等的情况的话,那么我们就同时进行++操作

因为要求里面返回结果的元素都必须是唯一的

所以我们需要利用set进行去重的操作的


142.环形链表 II

142.环形链表 II

在这里插入图片描述
我们之前的思路是:

先利用快慢指针,直到我们的快慢指针相遇我们就停下来

然后在头节点创建一个指针,让这个指针和我们的慢指针一起运动,,直到我们的头结点指针遇到了慢指针我们就停下

那么当这两个指针相遇的时候我们就将当前的位置直接返回就行了,当前的位置就是我们所需要的入环节点处

那么我们这里的思路是将每个节点都放到我们的set里面,使用insert进行插入的操作

如果某个节点插入失败的话,那么就说明这个节点在set中已经是存在了的,那么我们直接阿静这个节点返回,这个节点就是我们所需要的环形链表的入环的第一个节点

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution 
{
public:ListNode *detectCycle(ListNode *head){set<ListNode*>s;ListNode*cur=head;while(cur){if(s.count(cur))//如果这个节点在的话,就是说这个节点在set中已经存在了,那么这个count就会返回一个1{return cur;//我们直接把这个节点返回了,这个就是把环形链表的入环的第一个节点}else//如果不存在的话那么我们就进行插入的操作{s.insert(cur);}cur=cur->next;}return nullptr;//如果出了循环的话还没有结果的话那么就是不带环的,那么我们直接返回空就行了}
};

我们这里的思路就是使用cur进行遍历链表

然后再使用set中的count进行节点是否存在进行判断,如果存在的话就返回1,那么我们就字节返回这个节点,因为set中已经存在了一个相同的节点,如果再出现一次的话,那么我们直接就将这个节点返回了,因为这个节点是我们的环形链表的环节点那里的第一个节点

然后如果没有出现的话,我们就会进入到下面的else的语句中,我们直接进行插入,将节点直接插入到set实例化的对象s中去,

如果出了循环还没有结果返回的话,那么就说明这个链表不是带环的链表,那么我们直接返回空就行了

Map

map的概念

map的声明如下,Key就是map底层关键字的类型,T是map底层value的类型,set默认要求Key支持小于比较,如果不支持或者需要的话可以自行实现仿函数传给第二个模版参数,map底层存储数据的内存是从空间配置器申请的。一般情况下,我们都不需要传后两个模版参数。map底层是用红黑树实现,增删查改效率是O(logN),迭代器遍历是走的中序,所以是按key有序顺序遍历的。

template < class Key,                                     // map::key_typeclass T,                                       // map::mapped_typeclass Compare = less<Key>,                     // map::key_compareclass Alloc = allocator<pair<const Key,T> >    // 
map::allocator_type> class map;

pair类型介绍

map的底层是红黑树节点中的数据,使用pair<Key,T>存储键值对数据

typedef pair<const Key, T> value_type;
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){}template<class U, class V> pair (const pair<U,V>& pr): first(pr.first), second(pr.second){}
};
template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{return ( pair<T1,T2>(x,y) );
}

map的构造

map的构造我们关注以下几个接口即可。

map的支持正向和反向迭代遍历,遍历默认按key的升序顺序,因为底层是二叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围for,map支持修改value数据,不支持修改key数据,修改关键字数据,破坏了底层搜索树的结构。

// empty (1)无参默认构造
explicit map (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
// range (2)迭代器区间   
template <class InputIterator>map (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());
// copy (3)拷贝构造   
map (const map& x);
// initializer list (5) initializer   
map (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());//   迭代器是一个双向迭代器
iterator   -> a bidirectional iterator to const value_type
//正向迭代器   
iterator begin();
iterator end();
//反向迭代器   
reverse_iterator rbegin();
reverse_iterator rend();

map的增删查

通过隐士类型转换的直接插入是最简单的

int main()
{map<string, string>dict = { {"left","左边"},{"right","右边"} ,{"insert","插入"} ,{"string","字符串"} };pair<string, string>kv1("first", "第一个");dict.insert(kv1);//匿名对象隐式类型转换dict.insert(pair<string, string>("first", "第一个"));//我们调用make_pair这个模版参数将我们传的两个参数的模版进行推导出来了//我们使用make_pair就不用进行模版参数的声明了,直接让他们推//make_pair是一个函数模版dict.insert(make_pair("sort", "排序"));dict.insert({ "auto","自动的" });//多参数的隐士类型转换就行了,这种是最简单的for (auto& e : dict)//加上引用的话,那么就相当于将*it给到e{//不加引用的话拷贝构造的时间就会很多了cout << e.first << ":" << e.second << endl;}return 0;
}

通过加等的函数赋值重载的话我们是可以实现这个数据的修改操作的

但是只能对second进行修改的操作的,不能对first进行修改的

在这里插入图片描述

find函数的返回值

find 函数是 C++ 标准库中的 std::mapstd::unordered_map 容器提供的一个方法,用来在容器中查找指定的键。它返回的是一个迭代器

具体来说:

find 函数的行为

auto ret = map.find(key);
1. 如果键 key 存在:
  • 返回一个指向 key 所对应键值对的迭代器。

  • 通过这个迭代器,可以访问键和它的值:

ret->first // 键 (key)
ret->second // 值 (value)

#### 2. 如果键 `key` 不存在:- 返回一个特殊的迭代器:`map.end()`。- `map.end()` 表示容器的尾后位置,不指向任何有效元素。- 所以可以用以下条件来判断键是否存在:```C++
if (ret == map.end()) {// 键不存在
} else {// 键存在
}

举例说明

#include <iostream>
#include <map>
#include <string>using namespace std;int main() {map<string, int> countMap = {{"apple", 2}, {"banana", 3}};// 查找键 "apple"auto it = countMap.find("apple");if (it != countMap.end()) {cout << "Found: " << it->first << " -> " << it->second << endl;} else {cout << "Key not found" << endl;}// 查找键 "orange"auto it2 = countMap.find("orange");if (it2 != countMap.end()) {cout << "Found: " << it2->first << " -> " << it2->second << endl;} else {cout << "Key not found" << endl;}return 0;
}
输出结果:
Found: apple -> 2
Key not found

总结:

  • find(key) 返回一个迭代器:

    • 指向 key 对应的键值对(如果找到)。

    • 等于 map.end()(如果没找到)。

  • 通过 ret->firstret->second 可以访问键值对中的键和值。

  • 常用于判断键是否存在或直接操作键值对。

map的构造遍历以及增删查使用详例

*it 是 map 中当前迭代器指向的元素,这个元素是一个 pair 类型,其中包含了 key-value 键值对。

(*it).first:访问当前键值对中的 键(key)。

(*it).second:访问当前键值对中的 值(value)。

当然我们也是可以使用范围for的

#include<iostream>
using namespace std;
#include<map>
int main()
{pair<string, string> kv1 = { "left","左边" };//键值对//imnitializer_list构造及迭代遍历map<string, string>dict = { {"left","左边"},{"right","右边"} ,{"insert","插入"} ,{"string","字符串"} };//将键值对使用map进行存储起来//	auto it = dict.begin();map<string, string>::iterator it = dict.begin();while (it != dict.end()){//cout << (*it).first << ":"<< (*it).second<<endl;//pair这个类型不支持流插入的cout << it->first << ":" << it->second << endl;//pair这个类型不支持流插入的//这个箭头是运算符重载的箭头,数据的指针,一个pair的指针++it;}//pair这个类型是不支持流插入的cout << endl;for (auto& e : dict)//加上引用的话,那么就相当于将*it给到e{//不加引用的话拷贝构造的时间就会很多了cout << e.first << ":" << e.second << endl;}return 0;
}
//*it 是 map 中当前迭代器指向的元素,这个元素是一个 pair 类型,其中包含了 key-value 键值对。
//(*it).first:访问当前键值对中的 键(key)。
//(*it).second:访问当前键值对中的 值(value)。

map中的operator[]的使用

在这里插入图片描述
insert除了插入还有查找的功能

插入成功的话就返回插入成功的位置的迭代器,找到这个king的节点

插入失败也会返回king位置节点的迭代器的

first是迭代器的

second是是否插入成功,只可能是true或者是false

在这里插入图片描述

int main3()
{map<string, string>dict;dict.insert(make_pair("sort", "插入"));//key不存在->插入dict["insert"];//左边dict["left"] = "左边";//修改dict["left"] = "左边、剩余";cout << dict["left"] << endl;return 0;
}
int main()
{string arr[] = { "苹果","西瓜","西瓜" ,"西瓜" ,"苹果" ,"香蕉","香蕉" };map<string, int>countMap;//第二个参数是次数//遍历这个数组中的所有水果for (const auto& str : arr){//auto ret = countMap.find(str);find函数返回一个特殊的迭代器:map.end()。map.end() 表示容器的尾后位置,不指向任何有效元素。//if (ret == countMap.end())//find函数返回一个指向 key 所对应键值对的迭代器。就是说明不在//{//	countMap.insert({ str,1 });//那么我们进行插入的操作//}//else//{//	ret->second++;//那么我们进行这个次数的增加就行了//}countMap[str]++;}for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}//结构化绑定声明//for (const auto& [key, value] : countMap)//{//	cout << key << ": " << value << endl;//}//ey:表示 map 中的键(std::map 的第一个成员 first)。//value:表示 map 中的值(std::map 的第二个成员 second)。//结构化绑定的语法可以直接将 std::pair 解包成独立的变量,避免使用 it->first 和 it->second 的冗长语法。return 0;
}

题目

138.随机链表的复制

138.随机链表的复制

在这里插入图片描述


这道题的目的是实现对一个包含随机指针的链表的深拷贝,并且需要保证新链表中每个节点的 randomnext 指针正确指向对应的新节点。

692. 前K个高频单词

692. 前K个高频单词
在这里插入图片描述

class Solution {
public:vector<string> topKFrequent(vector<string>& words, int k){//按照次数进行比较//仿函数struct Compare{//比较两个键值对 kv1 和 kv2bool operator()(const pair<string,int>&kv1,const pair<string,int>&kv2){return kv1.second>kv2.second;}};//统计处次数//使用 map 记录单词的频率map<string,int>countMap;//遍历输入单词列表 words,每遇到一个单词,就增加它在 countMap 中对应的计数值。for(auto&str:words){countMap[str]++;}//sort是不能对map进行排序的//利用迭代器区间进行初始化操作//我们将这个map中的数据存储在vector中,利用迭代器//map 是有序的,但不支持直接排序。//将 map 的内容通过迭代器范围转换为一个 vector,以便后续对 vector 进行排序。vector<pair<string,int>>v(countMap.begin(),countMap.end());//sort默认是升序,我们期望排降序,但是是不稳定的,//stable_sort就是稳定的//使用 stable_sort 对 vector 进行排序,保证当两个单词频率相同时,按字典序排序stable_sort(v.begin(),v.end(),Compare());//然后进行排序的操作//创建一个结果向量 retv,从排序后的 vector 中取出前 k 个单词(即频率最高的单词vector<string>retv;for(size_t i=0;i<k;++i){retv.push_back(v[i].first);}return retv;}
};/*
统计频率:
countMap = { "i": 2, "love": 2, "leetcode": 1, "coding": 1 }
排序后:
v = [("i", 2), ("love", 2), ("coding", 1), ("leetcode", 1)]
取前 2 个:
结果为 ["i", "love"]。
*///std::sort(起始迭代器, 结束迭代器, 比较器);

使用 stable_sort 对 vector 进行排序,保证当两个单词频率相同时,按字典序排序

还有一种解决方法

我们在这个仿函数中多添加一种情况

次数大的在前面

次数相等的时候我们的字典数小的在前面,那么我们的后续排序可以对稳定性没有要求了

我们直接手动控制仿函数

class Solution {
public:vector<string> topKFrequent(vector<string>& words, int k){//按照次数进行比较//仿函数struct Compare{//比较两个键值对 kv1 和 kv2bool operator()(const pair<string,int>&kv1,const pair<string,int>&kv2){return kv1.second>kv2.second||//次数大的在前面(kv1.second==kv2.second&&kv1.first<kv2.first);//次数相等的时候我们期望字典数小的在前面}};//统计处次数//使用 map 记录单词的频率map<string,int>countMap;//遍历输入单词列表 words,每遇到一个单词,就增加它在 countMap 中对应的计数值。for(auto&str:words){countMap[str]++;}//sort是不能对map进行排序的//利用迭代器区间进行初始化操作//我们将这个map中的数据存储在vector中,利用迭代器//map 是有序的,但不支持直接排序。//将 map 的内容通过迭代器范围转换为一个 vector,以便后续对 vector 进行排序。vector<pair<string,int>>v(countMap.begin(),countMap.end());//sort默认是升序,我们期望排降序,但是是不稳定的,//stable_sort就是稳定的//使用 stable_sort 对 vector 进行排序,保证当两个单词频率相同时,按字典序排序sort(v.begin(),v.end(),Compare());//然后进行排序的操作//创建一个结果向量 retv,从排序后的 vector 中取出前 k 个单词(即频率最高的单词vector<string>retv;for(size_t i=0;i<k;++i){retv.push_back(v[i].first);}return retv;}
};/*
统计频率:
countMap = { "i": 2, "love": 2, "leetcode": 1, "coding": 1 }
排序后:
v = [("i", 2), ("love", 2), ("coding", 1), ("leetcode", 1)]
取前 2 个:
结果为 ["i", "love"]。
*///std::sort(起始迭代器, 结束迭代器, 比较器);

pair的具体使用

pair也是模版

存储键值对的

std::pair 是 C++ 标准模板库 (STL) 提供的一个非常方便的工具类,用于存储两个相关联的值。它是一个模板类,可以存储不同类型的两个值。以下是对 pair 使用的详细介绍。


1. pair 的基本定义

std::pair 的定义如下:

template <class T1, class T2>
struct pair {T1 first;T2 second;
};
  • first:表示第一个元素。

  • second:表示第二个元素。

它是一个简单的容器,可以将两个数据绑定在一起。


2. 创建和初始化 pair

方法 1:使用构造函数
#include <iostream>
#include <utility> // 包含 pairint main() {std::pair<int, std::string> p(1, "apple");std::cout << p.first << " " << p.second << std::endl;return 0;
}

输出:

1 apple
方法 2:使用 std::make_pair
#include <iostream>
#include <utility>int main() {auto p = std::make_pair(1, "banana");std::cout << p.first << " " << p.second << std::endl;return 0;
}

输出:

1 banana
方法 3:使用列表初始化
#include <iostream>
#include <utility>int main() {std::pair<int, double> p = {3, 3.14};std::cout << p.first << " " << p.second << std::endl;return 0;
}

输出:

3 3.14

3. 修改 pair 的值

#include <iostream>
#include <utility>int main() {std::pair<int, std::string> p(10, "orange");p.first = 20;                // 修改 first 的值p.second = "grape";          // 修改 second 的值std::cout << p.first << " " << p.second << std::endl;return 0;
}

输出:

20 grape

4. pair 的常见用法

(1) 在容器中使用

pair 通常与 STL 容器(如 std::mapstd::vector)结合使用。

map 中使用

std::map 使用 pair 来存储键值对:

#include <iostream>
#include <map>int main() {std::map<std::string, int> fruit_count;fruit_count["apple"] = 3;fruit_count["banana"] = 5;for (const auto &entry : fruit_count) {std::cout << entry.first << " " << entry.second << std::endl;}return 0;
}

输出:

apple 3
banana 5
vector 中使用

std::vector 可以存储 pair 元素:

#include <iostream>
#include <vector>
#include <utility>int main() {std::vector<std::pair<std::string, int>> v = {{"apple", 3}, {"banana", 5}, {"cherry", 2}};for (const auto &p : v) {std::cout << p.first << ": " << p.second << std::endl;}return 0;
}

输出:

apple: 3
banana: 5
cherry: 2

(2) 用于返回多个值

pair 可以用来从函数返回多个值:

#include <iostream>
#include <utility>std::pair<int, int> find_min_max(const std::vector<int>& nums) {int min_val = nums[0], max_val = nums[0];for (int num : nums) {if (num < min_val) min_val = num;if (num > max_val) max_val = num;}return {min_val, max_val};
}int main() {std::vector<int> nums = {3, 1, 4, 1, 5, 9};auto result = find_min_max(nums);std::cout << "Min: " << result.first << ", Max: " << result.second << std::endl;return 0;
}

输出:

Min: 1, Max: 9

5. pair 的比较操作

pair 支持按字典序的比较:

  • 首先比较 first,如果 first 相等,再比较 second

  • 支持 ==, !=, <, >, <=, >= 等操作符。

#include <iostream>
#include <utility>int main() {std::pair<int, int> p1 = {1, 2};std::pair<int, int> p2 = {1, 3};if (p1 < p2) {std::cout << "p1 is less than p2" << std::endl;}return 0;
}

输出:

p1 is less than p2

6. 常见问题

(1) 自定义排序 pair

可以结合 sortstable_sort 使用自定义比较规则:

#include <iostream>
#include <vector>
#include <algorithm>bool compare(const std::pair<int, int>& a, const std::pair<int, int>& b) {return a.second > b.second; // 按 second 降序
}int main() {std::vector<std::pair<int, int>> v = {{1, 3}, {2, 2}, {3, 5}};std::sort(v.begin(), v.end(), compare);for (const auto& p : v) {std::cout << "(" << p.first << ", " << p.second << ") ";}return 0;
}

输出:

(3, 5) (1, 3) (2, 2)

总结

  1. pair 用于存储两个值,可以是不同的类型,方便关联信息的存储。

  2. 常见操作:初始化、修改值、结合容器(如 mapvector)使用。

  3. 支持比较操作,方便排序和查找。

  4. 可以结合 std::make_pair 或列表初始化简化代码。

如果你还有更具体的问题,可以进一步探讨!

相关文章:

【C++ map和set】数据的吟游诗:Map与Set的双城记

公主请阅 set1.序列式容器和关联式容器2.set的介绍3.set的构造和迭代器部分set可以进行去重操作的&#xff0c;在去重的同时可以对插入进来的数字进行排序的操作4.set的增删查inserterasefindupper_bound和 lower_bound 5.multiset和set的差异6相关题目349.两个数组的交集142.环…...

leetcode 之 二分查找(java)(3)

文章目录 5. 81. 搜索旋转排序数组 II6. 378、有序矩阵中第k个小的元素 5. 81. 搜索旋转排序数组 II 题目描述&#xff1a; 已知存在一个按非降序排列的整数数组 nums &#xff0c;数组中的值不必互不相同。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#…...

后端返回前端的数据量过大解决方案

后端返回前端的数据量过大解决方案 性能面板(Performance) chrome调试指南 原因 遇到一个页面有好几个表格&#xff0c;部分表格采用虚拟滚动条 数据量有点大 接近快60s了&#xff0c;看一下是哪里导致的慢 后台请求方法执行并不慢 2024-12-04 15:21:52.889 INFO 69948 …...

STL算法之其它算法_下

random_shuffle 这个算法将[first,last)的元素次序随机排列。也就说&#xff0c;在N!中可能的元素排列中随机选出一种&#xff0c;此处N为last-first。 N个元素的序列&#xff0c;其排列方式为N!中&#xff0c;random_shuffle会产生一个均匀分布&#xff0c;因此任何一个排列被…...

MySQL如何区分幻读和不可重复读

在MySQL中&#xff0c;幻读和不可重复读都是并发事务中可能出现的问题&#xff0c;但它们的表现和原因略有不同。 不可重复读 (Non-Repeatable Read) 不可重复读是指在同一个事务内&#xff0c;多次读取同一行数据时&#xff0c;可能会得到不同的结果。这种情况发生在一个事务…...

html ul li 首页渲染多条数据 但只展示八条,其余的数据全部隐藏,通过icon图标 进行展示

<div style"float: left;" id"showMore"> 展开 </div> <div style"float: left;“id"hideLess"> 收起 </div> var data document.querySelectorAll(.allbox .item h3 a); const list document.querySelectorAl…...

Vue3安装 运行教程

本文是综合了所有vue安装教程而成 更细化 更简略 希望对各位读者有所帮助&#xff01; Vue安装 1. Vue-cli脚手架安装 安装vue的方式有很多 我们这里选择npm方式安装vue npm方式 npm方式安装vue&#xff0c;详细介绍见下文。 1.node.js安装和配置 安装npm 需要安装note.js&…...

Spring事务的一道面试题

每次聊起Spring事务&#xff0c;好像很熟悉&#xff0c;又好像很陌生。本篇通过一道面试题和一些实践&#xff0c;来拆解几个Spring事务的常见坑点。 原理 Spring事务的原理是&#xff1a;通过AOP切面的方式实现的&#xff0c;也就是通过代理模式去实现事务增强。 具体过程是&a…...

PHP SM4 加密

PHP SM4 加密 sm4基类 class Sm4 {private $ck [0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,0xc0c7ced5, 0xdce3ea…...

群控系统服务端开发模式-应用开发-短信工厂腾讯云短信开发

一、腾讯云短信工厂开发 1、添加框架对应的SDK composer require tencentcloud/tencentcloud-sdk-php 2、添加腾讯云工厂 在根目录下extend文件夹下Sms文件夹下channel文件夹下&#xff0c;创建腾讯云短信发送工厂并命名为TencentSmsSender。记住&#xff0c;一定要在腾讯云短…...

vue key属性强制刷新组件

在 Vue 中&#xff0c;key 属性通常用来帮助 Vue 跟踪每个组件或元素的身份&#xff0c;尤其是在使用 v-for 渲染列表时。当 key 值发生变化时&#xff0c;Vue 会销毁并重新渲染组件&#xff0c;这也可以用于强制刷新组件。 如果你想强制刷新一个组件&#xff0c;可以通过动态…...

浪潮X86服务器NF5280、8480、5468、5270使用inter VROC Raid key给NVME磁盘做阵列

浪潮服务器inter VROC Raid key给NVME磁盘做阵列方法 Inter VROC技术简介Raid Key 授权&#xff0c;即VROC SKU兼容性处理器兼容性列表平台和芯片组兼容性列表各Raid级别最大磁盘数量硬盘型号操作系统 服务器上的操作安装Raid Key确认服务器能识别硬盘识别磁盘所在的通道及服务…...

最长最短单词

最长最短单词 C语言实现C实现Java实现Python实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输入1行句子&#xff08;不多于200个单词&#xff0c;每个单词长度不超过100&#xff09;&#xff0c;只包含字母、空格和逗号。单词由至少一…...

Muduo网络库剖析 --- 架构设计

文章目录 前言概述篇一、Muduo网络库简介Reactor事件处理模式 二、基于muduo实现简易聊天服务器实现测试运行 三、muduo的架构设计Reacor模式muduo框架架构解析主从Reactor工作流程主线程(main Reactor)工作线程(sub Reactor) 线程分配与负载均衡 四、总结线程分配与负载均衡 四…...

lwip raw、netcoon、socket三种接口编程的区别

目录 一、前言 二、LWIP 简介 三、LWIP RAW 编程 1.概念与原理 2.编程模型与流程 3.示例代码 4.优点与缺点 四、LWIP NETCONN 编程 1.概念与原理 2.编程模型与流程 3.示例代码 4.优点与缺点 五、LWIP SOCKET 编程 1.概念与原理 2.编程模型与流程 3.示例代码 …...

在办公室环境中用HMD替代传统显示器的优势

VR头戴式显示器&#xff08;HMD&#xff09;是进入虚拟现实环境的一把钥匙&#xff0c;拥有HMD的您将能够在虚拟现实世界中尽情探索未知领域&#xff0c;正如如今的互联网一样&#xff0c;虚拟现实环境能够为您提供现实中无法实现的或不可能实现的事。随着技术的不断进步&#…...

P3916 图的遍历(Tarjan缩点和反向建边)

P3916 图的遍历 - 洛谷 | 计算机科学教育新生态 写法一&#xff1a;Tarjan 思路&#xff1a;先运用Tarjan算法得到每个连通块中最大的编号&#xff0c;然后对每个连通块进行缩点重新建图&#xff0c;进行dfs&#xff0c;得到缩点后的连通块能够达到的最大编号。 Code: conste…...

Element UI 的 el-tree 组件e中默认展开前两层,设置 default-expanded-keys 属性来实现

在使用 Element UI 的 el-tree 组件时&#xff0c;如果你希望默认展开树的前两层节点&#xff0c;可以通过设置 default-expanded-keys 属性来实现。这个属性接受一个数组&#xff0c;数组中的值是需要默认展开的节点的 key。 首先&#xff0c;你需要确保你的每个树节点都有唯…...

Vue 项目中未登录状态如何统一处理

在 Vue 项目中&#xff0c;处理未登录状态&#xff08;比如用户访问需要登录的页面时&#xff09;是一项常见的需求。为了实现这一需求&#xff0c;我们通常使用 Vue Router 配合 Vuex 或者 Vue 的全局状态管理来统一处理未登录的状态&#xff0c;确保用户只能访问允许的页面。…...

Java 集合:强大的数据管理工具

在 Java 编程中&#xff0c;集合是一种非常重要的工具&#xff0c;它提供了一种方便的方式来存储和操作一组对象。本文将深入探讨 Java 集合框架&#xff0c;包括其主要类型、特点、用法以及一些最佳实践。 一、引言 在软件开发过程中&#xff0c;我们经常需要处理一组数据。…...

Creating Server TCP listening socket *:6379: bind: No error

启动redis报错&#xff1a;Creating Server TCP listening socket *:6379: bind: No error 解决方案&#xff1a; 1、直接在命令行中输入 redis-cli.exe 2、输入shutdown&#xff0c;关闭 3、输exit&#xff0c;退出 4、重新输入 redis-server.exe redis.windows.conf&…...

iOS免费共享企业证书、苹果最新企业证书免费获取

前言 大家可能都注意到了&#xff0c;苹果手机和安卓手机在安装软件上有点不一样。如果你在苹果手机上想装那些没在官方商店&#xff08;App Store&#xff09;里的软件&#xff0c;那就得给它们“签个名”&#xff0c;就像是给它们盖个章&#xff0c;这样手机才能认识它们&am…...

如果用Python写爬虫,具体怎么实现随机请求间隔呢?

在Python中实现随机请求间隔&#xff0c;通常使用time.sleep()函数结合random模块来生成随机的等待时间。以下是一个具体的实现方法&#xff1a; 导入必要的模块 首先&#xff0c;你需要导入time和random模块&#xff1a; import time import random 设置随机间隔 然后&am…...

aws(学习笔记第十五课) 如何从灾难中恢复(recover)

aws(学习笔记第十五课) 如何从灾难中恢复 学习内容&#xff1a; 使用CloudWatch对服务器进行监视与恢复区域(region)&#xff0c;可用区(available zone)和子网(subnet)使用自动扩展(AutoScalingGroup) 1. 使用CloudWatch对服务器进行监视与恢复 整体架构 这里模拟Jenkins Se…...

nginx4层限速

Nginx的功能概述 Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也可以作为邮件代理服务器等。它主要工作在7层&#xff08;应用层&#xff09;&#xff0c;但在某些场景下也可以实现部分4层&#xff08;传输层&#xff09;的功能。 关于4层限速 Nginx自身的限制&#x…...

Spring Cloud Alibaba 之 “Feign多参数构造”

在上一篇文章整合好了Feign&#xff0c;现在来总结以下Feign调用多参数方法的使用。 GET方式&#xff1a; Spring Cloud为Feign支持了Spring Mvc注解的。如果请求的是localhost:8083/test?id1&namecoco,那么如果我们这样写&#xff08;User实体类有这二个属性&#xff09…...

C#高级教程

目录 C# 特性&#xff08;Attribute&#xff09;C# 反射&#xff08;Reflection&#xff09;C# 属性&#xff08;Property&#xff09;C# 索引器&#xff08;Indexer&#xff09;C# 委托&#xff08;Delegate&#xff09;C# 事件&#xff08;Event&#xff09;C# 集合&#xf…...

c++ 位图和布隆过滤器

位图&#xff08;bitmap&#xff09; 定义 位图是一种使用位数组存储数据的结构。每一位表示一个状态&#xff0c;通常用于快速判断某个值是否存在&#xff0c;或者用来表示布尔类型的集合。 特点 节省空间&#xff1a;一个字节可以表示8个状态。高效操作&#xff1a;位操作…...

基于Springboot开发的云野旅游平台

一、功能介绍 云野旅游平台包含管理员、用户两个角色以及前后台系统。 前台系统功能 用户登录成功后&#xff0c;可以进行查看旅游路线、最新线路、旅游资讯、个人中心、后台管理、购物车、客服等功能模块。进行相对应操作。 后台系统功能 管理员或用户登录成功后&#xf…...

微服务即时通讯系统(5)用户管理子服务,网关子服务

用户管理子服务&#xff08;user文件&#xff09; 用户管理子服务也是这个项目中的一个业务最多的子服务&#xff0c;接口多&#xff0c;但是主要涉及的数据表只有user表&#xff0c;Redis的键值对和ES的一个搜索引擎&#xff0c;主要功能是对用户的个人信息进行修改管理&#…...

docker.io连接超时的处理,用代理网站

docker pull的时候会超时&#xff1a; Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 这时可以找一些代理网站&#xff0c;比如…...

【测试工具JMeter篇】JMeter性能测试入门级教程(四):JMeter中BeanShell内置方法使用

一、什么是BeanShell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法;BeanShell是一种松散类型的脚本语言(这点和JS类似);BeanShell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器,具有对象脚本语言特性,非常精简…...

JavaScript 键盘控制移动

如果你想通过 JavaScript 实现键盘控制对象&#xff08;比如一个方块&#xff09;的移动&#xff0c;下面是一个简单的示例&#xff0c;展示如何监听键盘事件并根据按下的键来移动一个元素。 HTML 和 CSS&#xff1a; <!DOCTYPE html> <html lang"en">…...

如何预防服务器后台爆破攻击

服务器后台爆破&#xff08;Brute Force Attack&#xff09;是一种通过反复尝试用户名和密码组合&#xff0c;以非法获取系统访问权限的攻击方式。这种攻击不仅会消耗服务器资源&#xff0c;还可能导致合法用户被锁定或敏感数据泄露。为了有效预防服务器后台爆破攻击&#xff0…...

AI 写作(一):开启创作新纪元(1/10)

一、AI 写作&#xff1a;重塑创作格局 在当今数字化高速发展的时代&#xff0c;AI 写作正以惊人的速度重塑着创作格局。AI 写作在现代社会中占据着举足轻重的地位&#xff0c;发挥着不可替代的作用。 随着信息的爆炸式增长&#xff0c;人们对于内容的需求日益旺盛。AI 写作能够…...

【HarmonyOS】鸿蒙应用使用lottie动画

【HarmonyOS】鸿蒙应用使用lottie动画 一、lottie动画是什么&#xff1f; https://airbnb.design/lottie Lottie是由Airbnb团队开发的一个适用于iOS、Android、React Native、Web和Windows的开源动画库&#xff0c;用于解析使用Bodymovin导出为JSON的Adobe After Effects动…...

SQL面试题——腾讯SQL面试题 合并连续支付订单

合并连续支付订单 现有一张用户支付表:user_pay包含字段订单ID,用户ID,商户ID,支付时间,支付金额。如果同一用户在同一商户存在多笔订单,且中间该用户没有其他商户的支付记录,则认为是连续订单,请把连续订单进行合并,时间取最早支付时间,金额求和。 +----------+------…...

【docker】10. 容器操作案例

容器操作案例 容器基本操作 • 通过 nginx 镜像文件创建容器 • 容器的列举(包含正在运行的容器) # 发现此时 e7c33d9f5c61 这个容器运行的状态为 Up,即运行状态 rootLAPTOP-H2EI4I6A:~# docker container ls CONTAINER ID IMAGE COMMAND CREATED …...

postman测试

当然&#xff0c;以下是针对你提供的API层和Service层代码中涉及到的各个接口&#xff0c;如何使用 Postman 进行详细测试的指南。这个指南将帮助你理解如何配置 Postman 来测试这些接口&#xff0c;包括请求的构造、认证的处理、以及如何解读响应。 目录 准备工作接口测试指…...

【攻防实验】溯源与取证分析实验

溯源与取证分析实验 溯源取证分析作为网络攻防过程中重要环节&#xff0c;准确找到攻击者的入侵线索(尤其是攻击突破口、攻击IP地址、域名、工具等信息)&#xff0c;对于企业或者团队安全运营团队来说都是必备技能。常规攻击取证过程中往往会结合流量、Web访问日志、终端系统或…...

【测试工具JMeter篇】JMeter性能测试入门级教程(七):JMeter断言

一、前言 在 JMeter 中&#xff0c;断言元件&#xff08;Assertion&#xff09;用于验证测试结果是否符合预期。断言元件可以检查服务器的响应数据&#xff0c;以确保它们符合期望的模式或值&#xff0c;从而验证性能测试脚本的正确性。断言元件通常在每个请求的响应中添加&am…...

Linux 常用命令

目录 一、ls 指令 二、pwd命令 三、cd 指令 1、cd 目录名 2、cd .. 返回上级目录 3、cd ~ 进入用户家目 4、cd - 返回最近访问目录 5、cd相对路径&&cd绝对路径 四、touch指令 五、mkdir指令 1、mkdir 目录名 创建一个目录 2、mkdir -p 递归创建多…...

汽车IVI中控OS Linux driver开发实操(二十八):回声消除echo cancellation和噪声消除Noise reduction

概述: 在当今高度互联的世界中,清晰的实时通信比以往任何时候都更重要。在远程团队会议期间,没有什么能像回声一样打断对话。当说话者听到他们的声音回响时,可能会分散注意力,甚至无法理解对话。即使是很小的回声也会产生很大的影响,仅仅25毫秒的振幅就足以造成声音干扰…...

003-SpringBoot整合Pagehelper

SpringBoot整合Pagehelper 一、引入依赖二、配置 application.yml三、配置 MybatisPlusConfig四、Controller五、ServiceImpl 一、引入依赖 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</ar…...

零基础学安全--shell练习

目录 用shell写一个计算器 测试​ 一些小问题 n阶乘数 测试 拓展 写⼀个Shell脚本去筛选出eth0⽹卡的ipv4地址&#xff0c;并赋值⼀个变量输出 测试 无限重启 用shell写一个计算器 read -p "请输入数字a: " number1 read -p "请输入操作符&#xf…...

【专题】计算机网络之运输层(传输层)

1. 运输层协议概述 1.1 进程之间的通信 (1) 运输层的作用 运输层提供进程间的逻辑通信。 运输层的屏蔽作用&#xff1a; 运输层向高层用户屏蔽了下面网络核心的细节&#xff08;如网络拓扑、所采用的路由选择协议等&#xff09;&#xff0c;使应用进程看见的就是好像在两个运…...

【leetcode100】旋转图像

1、题目描述 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],…...

软件工程——期末复习(1)

名词解释&#xff1a; 名词解释--人月 答案&#xff1a;人月是软件开发工作量的单位&#xff0c;1人月表示1个程序员1个月的工作时间所开发的代码量。 请解释软件缺陷、错误和失败&#xff0c;并简单举例说明。 答案&#xff1a;缺陷&#xff08;defect&#xff09;指系统代…...

HTML5系列(3)--多媒体标签详解

前端技术探索系列&#xff1a;HTML5 多媒体标签详解 &#x1f3a5; 开篇寄语 &#x1f44b; 前端开发者们&#xff0c; 在前三篇文章中&#xff0c;我们探讨了 HTML5 的语义化和表单特性。今天&#xff0c;让我们深入了解 HTML5 的多媒体能力&#xff0c;看看如何构建强大的…...

Spring Boot 3.4.0 发布:功能概览与示例

Spring Boot 3.4.0 带来了许多增强功能&#xff0c;使现代应用开发更加高效、便捷和强大。以下是最新功能的完整概述&#xff0c;以及一些帮助您快速入门的代码示例。 1. 应用程序版本管理 Spring Boot 引入了 spring.application.version 属性&#xff0c;方便开发者设置和访…...