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

公主请阅

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的底层是搜索树,所以我们是不能进行修改的

C++ 复制代码
// 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

C++ 复制代码
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

C++ 复制代码
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,为的是寻找子串以及其他类型的字符串

C++ 复制代码
	//算法库的find,时间复杂度O(N)
	auto pos1 = find(s.begin(), s.end(), x);

	//set自身实现的查找O(logN)
	auto pos2 = s.find(x);

upper_bound和 lower_bound

C++ 复制代码
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可以直接将所以的这个数字删除了,一个不剩

C++ 复制代码
#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给值时会删除所有的x
	s.erase(x);
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

6相关题目

349.两个数组的交集

349.两个数组的交集

C++ 复制代码
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中已经是存在了的,那么我们直接阿静这个节点返回,这个节点就是我们所需要的环形链表的入环的第一个节点

C++ 复制代码
/**
 * 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有序顺序遍历的。

C++ 复制代码
template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // 
map::allocator_type
           > class map;

pair类型介绍

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

C++ 复制代码
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数据,修改关键字数据,破坏了底层搜索树的结构。

C++ 复制代码
// 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的增删查

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

C++ 复制代码
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 函数的行为

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

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

    C++ 复制代码

ret->first // 键 (key)

ret->second // 值 (value)

#### 2. 如果键 `key` 不存在:

- 返回一个特殊的迭代器:`map.end()`。

- `map.end()` 表示容器的尾后位置,不指向任何有效元素。

- 所以可以用以下条件来判断键是否存在:

  ```C++
if (ret == map.end()) {
    // 键不存在
} else {
    // 键存在
}

举例说明

C++ 复制代码
#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;
}
输出结果:
C++ 复制代码
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的

C++ 复制代码
#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

C++ 复制代码
int main3()
{
	map<string, string>dict;
	dict.insert(make_pair("sort", "插入"));

	//key不存在->插入
	dict["insert"];

	//左边
	dict["left"] = "左边";

	//修改
	dict["left"] = "左边、剩余";

	cout << dict["left"] << endl;

	return 0;
}
C++ 复制代码
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.随机链表的复制

C++ 复制代码

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

692. 前K个高频单词

692. 前K个高频单词

C++ 复制代码
class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k)
    {
        //按照次数进行比较
        //仿函数
        struct Compare
        {
            //比较两个键值对 kv1 和 kv2
            bool 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 进行排序,保证当两个单词频率相同时,按字典序排序

还有一种解决方法

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

次数大的在前面

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

我们直接手动控制仿函数

C++ 复制代码
class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k)
    {
        //按照次数进行比较
        //仿函数
        struct Compare
        {
            //比较两个键值对 kv1 和 kv2
            bool 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 的定义如下:

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

  • second:表示第二个元素。

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


2. 创建和初始化 pair

方法 1:使用构造函数
C++ 复制代码
#include <iostream>
#include <utility> // 包含 pair

int main() {
    std::pair<int, std::string> p(1, "apple");
    std::cout << p.first << " " << p.second << std::endl;
    return 0;
}

输出:

C++ 复制代码
1 apple
方法 2:使用 std::make_pair
C++ 复制代码
#include <iostream>
#include <utility>

int main() {
    auto p = std::make_pair(1, "banana");
    std::cout << p.first << " " << p.second << std::endl;
    return 0;
}

输出:

C++ 复制代码
1 banana
方法 3:使用列表初始化
C++ 复制代码
#include <iostream>
#include <utility>

int main() {
    std::pair<int, double> p = {3, 3.14};
    std::cout << p.first << " " << p.second << std::endl;
    return 0;
}

输出:

C++ 复制代码
3 3.14

3. 修改 pair 的值

C++ 复制代码
#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;
}

输出:

C++ 复制代码
20 grape

4. pair 的常见用法

(1) 在容器中使用

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

map 中使用

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

C++ 复制代码
#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;
}

输出:

C++ 复制代码
apple 3
banana 5
vector 中使用

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

C++ 复制代码
#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;
}

输出:

C++ 复制代码
apple: 3
banana: 5
cherry: 2

(2) 用于返回多个值

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

C++ 复制代码
#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;
}

输出:

C++ 复制代码
Min: 1, Max: 9

5. pair 的比较操作

pair 支持按字典序的比较:

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

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

C++ 复制代码
#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;
}

输出:

C++ 复制代码
p1 is less than p2

6. 常见问题

(1) 自定义排序 pair

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

C++ 复制代码
#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;
}

输出:

C++ 复制代码
(3, 5) (1, 3) (2, 2)

总结

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

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

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

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

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

相关推荐
大霞上仙1 小时前
pytest入门二:用例的执行顺序
开发语言·python·pytest
小团团02 小时前
Python中主要控制结构的详细说明
开发语言·python
Dxy12393102163 小时前
python制造一个报错
开发语言·python
流星白龙3 小时前
【C++算法】41模拟_外观数列
开发语言·c++·算法
taoyong0013 小时前
C/C++字符数组与字符串操作
c++
小丸子灬4 小时前
clipboard----封装复制组件
开发语言·javascript
小萌新~~~~4 小时前
在Scala中正则表达式的类型
开发语言·正则表达式·scala
洋芋爱吃芋头4 小时前
scala的泛型2
开发语言·后端·scala
悻运4 小时前
scala的泛型类
开发语言·后端·scala
2401_833788054 小时前
Scala函数的泛型
开发语言·后端·scala