【C++】map 和 set 的使用

本文介绍了C++中STL的关联式容器,如set、multiset和map、multimap,它们基于红黑树或哈希表实现,支持键值对存储和高效检索。set和multiset的区别、插入与删除操作以及map和multimap的key-value模型和特性都有详细讲解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

C++中的键值对

关联式容器

set、multiset

map、multimap


STL的容器分为两类:序列是容器、关联式容器;序列式容器是线性的数据结构,只有存储元素的功能,且像vector、list等还可以指定插入位置;而关联式容器底层底层的数据结构是较高级的红黑树、哈希表等,里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高,但是数据的插入不能像vector那样指定插入位置,它底层有自己的插入逻辑,这样才来保证高效的数据检索能力。

C++中的键值对

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

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)
{}
};

关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层数据结构,容器中的元素是一个有序的序列。今天具体学习树形结构的关联式容器的使用。

set、multiset

两个都属于key的搜索模型,虽然底层也是存储<value, value>构成的键值对,底层使用红黑树实现;set中插入元素时,只需要插入value即可,不需要构造键值对;元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。

区别在于,key不允许key冗余,而multikey允许

set的构造:

测试代码:

//set:key搜索模型
 void test_set1()
 {
     //功能:查找在不在
     //增、删、查;  set不支持修改
     set<int> s;
     s.insert(5);
     s.insert(2);
     s.insert(6);
     s.insert(1);
     s.insert(1);
     s.insert(1);
     pair<set<int>::iterator,bool> ret = s.insert(1);
     cout << ret.second << endl;

     set<int>::iterator it = s.begin();
     //中序遍历 底层是红黑树(前提是BST)
     while (it != s.end())
     {
         cout << *it << " ";
         ++it;
     }
     cout << endl;

     //用值删除
     s.erase(2);
     s.erase(20);//删除不存在的,无影响

     //用迭代器删除
     //找不到返回end
     set<int>::iterator it1 = s.find(3);
     //用迭代器时,不能删除不存在的
     if (it1 != s.end())
     {
         s.erase(it1);
     }

     for (auto e : s)
     {
         cout << e << " ";
     }
     cout << endl;

     if (s.count(3))
     {
         cout << "3在" << endl;
     }
     else
     {
         cout << "3不在" << endl;
     }
 }

 void test_set2()
 {
     set<int> s;
     set<int>::iterator itlow, itup;

     for (int i = 1; i <= 10; i++)
     {
         s.insert(i * 10);
     }

     //用法:删除区间[itlow,itup]
     itlow = s.lower_bound(25);//返回 >= val值的第一个迭代器
     itup = s.upper_bound(70);//返回 > val值的第一个迭代器

     s.erase(itlow, itup);
     for (auto e : s)
     {
         cout << e << " ";
     }
     cout << endl;

 }


 //multiset:变异的搜索树,允许相同值插入
 void test_set3()
 {
     //功能:查找在不在
      //增、删、查;  不支持修改
     multiset<int> s;
     s.insert(5);
     s.insert(2);
     s.insert(2);
     s.insert(6);
     s.insert(1);
     s.insert(1);
     s.insert(1);
     s.insert(1);
     s.insert(1);
     s.insert(1);

     for (auto e : s)
     {
         cout << e << " ";
     }
     cout << endl;

     //如果有多个值,find返回的是中序的第一个val的迭代器
     auto it = s.find(1);
     while (it != s.begin())
     {
         cout << *it << " ";
         ++it;
     }

     //[>= val, > val) 等到等于val的区间
     pair<multiset<int>::iterator, multiset<int>::iterator> ret = s.equal_range(1);
     //删除所有等于val的值
     s.erase(ret.first, ret.second);
     size_t count = s.erase(2);

     for (auto e : s)
     {
         cout << e << " ";
     }
     cout << endl;
    
 }

map、multimap

底层数据结构也是红黑树(平衡二叉树),在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair;

map不允许key冗余、支持下标访问符,即在[]中放入key,就可以找到与key对应的value;而multimap不支持下标访问,但允许key冗余。

map的构造:

测试代码:


 //map:key-value搜索模型
 void test_map1()
 {
     map<string,string> dict;
     //pair内部的一个构造是函数模板
     //插入的是一个pair对象
     dict.insert(pair<string, string>("sort", "排序"));
     dict.insert(pair<string, string>("insert", "插入"));
     dict.insert(pair<const char*, const char*>("lest", "左边"));

     string s1("xxx"), s2("yyy");
     dict.insert(make_pair(s1, s2));

     //常用;pair<const char*,const char*> 通过pair的模板构造自动推演参数类型,转换为pair<const string,string>
     dict.insert(make_pair("right", "右边"));

     //[]功能:插入、查找、修改val
     dict["erase"];//插入
     cout << dict["erase"] << endl;//查找
     dict["erase"] = "删除";//修改val
     dict["test"] = "测试";
     dict["left"] = "左边、剩余";
   
     map<string, string>::iterator it = dict.begin();
     while (it != dict.end())
     {
         //pair<string,string>为自定义类型,访问可以用*,也可以用-> 
         cout << (*it).first << "\t" << it->second << endl;
         ++it;
     }
     cout << endl;

     //加上引用,避免深拷贝引起性能消耗
     for (auto& kv : dict)
     {
         cout << kv.first << "\t" << kv.second << endl;
     }
 }

 //统计次数
 void test_map2()
 {
     string arr[] = { "苹果","苹果","苹果","香蕉","栗子"};
     map<string, int> countMap;

     for (auto& str : arr)
     {
         //以前的方式 && multimap的方式
         /*auto ret = countMap.find(str);
         if (ret == countMap.end())
         {
             countMap.insert(make_pair(str, 1));
         }
         else
         {
             ret->second++;
         }*/

         //更优雅的方式
         countMap[str]++;
     }

     for (auto& kv : countMap)
     {
         cout << kv.first << "\t" << kv.second << endl;
     }
 }

 //multimap 允许key冗余,不支持[],其余和map一样

其中,map的下标访问,底层是调用了insert

把文档里的代码拆解:

V& operator[](K& key)
{
    //[]重载函数中,先调用insert,返回了pair<iterator,bool>类型的迭代器
    //如果原来有元素,则插入失败,<指向原有元素的的迭代器,false>
    //如果插入新元素,则插入成功,<指向key的迭代器,true>
    pair<iterator,bool> ret =  insert(make_pair(key, V));
    //ret.first为pair<K,V>类型的迭代器,返回迭代器的val
    return ret.first->second;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值