【C++】速识map与set
本文是小编巩固自身而作,如有错误,欢迎指出!
目录
一、map和set是个啥
二、set
(1)lower_bound与upper_bound
(2)equal_range
(3)set的增删改
三、map
(1)key/value
(2)operator[]
一、map和set是个啥
在前文我们已经学习了二叉搜索树,而map和set实际上都是二叉搜索树的一种,关于二叉搜索树可以看前文:二叉搜索树https://blog.csdn.net/2401_85487070/article/details/151681422?fromshare=blogdetail&sharetype=blogdetail&sharerId=151681422&sharerefer=PC&sharesource=2401_85487070&sharefrom=from_link下面我们将其分别介绍。
二、set
在C++中,std::set 是标准模板库(STL)提供的一个容器,它基于红黑树(一种自平衡的二叉搜索树)实现,用于存储唯一元素,并且这些元素会按照一定的顺序自动排序(默认是升序)
总结来说就是三个词:中序、排序、去重
• set的声明如下,T就是set底层关键字的类型
• set默认要求T⽀持⼩于⽐较,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现仿函数传给第⼆个模 版参数
• set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第三个参 数。
• ⼀般情况下,我们都不需要传后两个模版参数。
• set底层是⽤红⿊树实现,增删查效率是O(logN) ,迭代器遍历是⾛的搜索树的中序,所以是有序 的。
(1)lower_bound与upper_bound
lower_bound
:用于在一个有序的序列(如std::vector
、std::set
等)中查找第一个不小于给定值的元素的位置。可以将其想象成在一排按顺序排列的书架上,找到第一本编号不小于你指定编号的书的位置。upper_bound
:用于在一个有序的序列中查找第一个大于给定值的元素的位置。就好像在书架上找到第一本编号大于你指定编号的书的位置。
int main() {std::set<int> v = {1, 3, 5, 7, 9, 11, 13};// 使用 lower_bound 查找第一个不小于 6 的元素auto low = std::lower_bound(v.begin(), v.end(), 6);if (low != v.end()) {std::cout << "lower_bound 找到的元素: " << *low << std::endl;}// 使用 upper_bound 查找第一个大于 6 的元素auto up = std::upper_bound(v.begin(), v.end(), 6);if (up != v.end()) {std::cout << "upper_bound 找到的元素: " << *up << std::endl;}return 0;
}
而有的同学就好奇了?这两个看起来功能差距不大,为什么要专门分开呢?,这里我们就要涉及一个新概念,multiset
std::multiset
是一种关联容器,它基于红黑树这种自平衡二叉搜索树实现。这意味着元素在容器中是有序存储的,默认按照元素的键值升序排列。
但其最大的特点就是可以存储重复元素。
我们将上述代码稍作修改就可以看到
std::set<int> v = { 1, 3, 5,6,6,6,6,7,9, 11, 13 };
(2)equal_range
equal_range 函数用于在一个已排序的范围内查找与给定键相等的所有元素。它返回一个 std::pair,其中 first 是指向第一个不小于给定键的元素的迭代器(等同于 lower_bound 的返回值),second 是指向第一个大于给定键的元素的迭代器(等同于 upper_bound 的返回值)。
int main() {std::multiset<int> ms = { 1, 2, 2, 3, 3, 3, 4 };auto range = ms.equal_range(3);for (auto it = range.first; it != range.second; ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
(3)set的增删改
insert:接收一个 initializer_list 作为参数
find:找到了就返回对应元素的迭代器,没找到返回end()count:返回找到的个数
erase:会造成迭代器失效
int main()
{set<int> s = { 1,2,3,9,11 };// 插入s.insert({ 4,5,6 });s.insert(7);s.insert(7);s.insert(7);// 搜索s.find(7); // 找到了返回对应键的迭代器if (s.find(8) == s.end()) // 没找到返回s.end(){cout << "没找到" << endl;}// 迭代器auto it = s.begin();while (it != s.end()){cout << *it << " ";it++;}cout << endl;// it = s.begin();// s.erase(it);// cout << "迭代器失效 :" << *it << endl;multiset<int> s2 = { 1,1,1,1,0,0,0 };cout << s2.count(1) << endl; // 返回找到的个数cout << s2.count(2) << endl;}
三、map
std::map 是一种关联容器,它存储的元素是键值对(std::pair<const Key, T>),其中键(Key)是唯一的,并且容器会根据键的大小对元素进行自动排序(默认按升序排列)。
而讲到map,就得涉及一个新知识key/value
(1)key/value
key
:只有key作为关键码,结构中只存储key,关键码即为需要搜索到的值,且key不支持修改。set
就是只存储键的。key/val
:存储键值对key: val
的,搜索和查找时,只根据key的值进行查找,可以修改key对应的val的值。map
就是存储键值对的。
我们看看下面代码就可以理解
void test01()
{pair<string, string> kv1("apple", "苹果");pair<string, string> kv2("banana", "香蕉");pair<string, string> kv3("orange", "橘子");map<string, string> dicrtory = { kv1,kv2,kv3 };dicrtory.insert(make_pair("money", "软妹币"));auto it = dicrtory.begin();while (it != dicrtory.end()){//it->second = "xyz";cout << (*it).first << ":" << (*it).second << endl;++it;}cout << endl;
}
在上述代码我们就可以看到,前面的英文名就是key(不可修改),后面的中文解释就是value(可以修改)。
(2)operator[]
std::map 的 [] 运算符用于通过键来访问或插入元素。如果指定的键已经存在于 map 中,[] 运算符会返回该键对应的值的引用;如果键不存在,[] 运算符会自动插入一个新的键值对,其中键是指定的键,值是该值类型的默认构造值,然后返回这个新插入值的引用。
map第⼀个⽀持修改的⽅式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map 还有⼀个⾮常重要的修改接⼝operator[],但是operator[]不仅仅⽀持修改,还⽀持插⼊数据和查找数 据,所以他是⼀个多功能复合接⼝
void blogtest(){std::map<std::string, int> scores;// 插入元素scores["Alice"] = 90;// 访问已存在的元素std::cout << "Alice's score: " << scores["Alice"] << std::endl;// 访问不存在的元素,会插入默认值std::cout << "Bob's score: " << scores["Bob"] << std::endl;}
下面我们讲解一下[]的原理
在官方界面,我们可以看到[]的返回值是
看起来非常的复杂令人头疼,下面我们就解释一下
首先,我们看看红色的框,这是一个insert函数,它的返回值是一个pair<ietrator,bool>(在insert后,成功会返回指针,失败会返回false),然后我们又取了他的firsrt,即insert返回的指针(iterator),然后我们对其进行解引用得到一个pair<key,value>,到这里相信大家都知道下一步是什么了,就是取出他的value,实现了我们平时看到的[]的作用。
本次分享就到这里结束了,后续会继续更新,感谢阅读!