set和map都是关联式容器
先介绍两个值:
键值:key 实值:value
set特性:所有的元素都会根据元素的键值(key)自动的被排序。对于set元素来说键值就是实值,实值就是键值,set不允许两个元素有相同的键值(我们不能通过set的迭代器去修改set的元素,原因后面会解释)
map特性:所有的元素都会根据元素的键值(key)自动的被排序,map的所有元素都是pair,同时拥有实值(value)和键值(key),pair的第一个元素被视为键值,第二个元素被视为实值,map不允许两个元素拥有相同键值。(可以通过map的迭代器修改实值,不能修改键值)。
实际上pair的结构体为:
template<class K,class V>
struct pair
{
k first;
v second;
}
set
我们先来看一下set的成员函数:
int a[5] = { 0, 1, 2, 4, 3 };
set<int> set1();//建立一个空set
set<int> set2(a, a + 5);//指定范围的set
set<int> set3(set2);//拷贝构造函数
set<int> set4(set2.begin(),set2.end());//使用了迭代器来构造set4
set<int> set5 = set3;//赋值运算符重载
迭代器:
set的迭代器(左闭右开)
这里begin是指向首元素,end指向的是最后一个元素的下一个位置
rbegin和rend是倒过来的。即,rbegin是最后一个元素,rend是首元素的上一个位置
cbegin和cend是const迭代器
crbegin和crend返回的是倒过来的const迭代器。
这里使用begin和end来举例子
set<int>::iterator it = s2.begin();
while (it != s2.end())
{
cout << *it << endl;
++it;
}
遍历刚才s2,输出结果
插入insert
s2.insert(7);
s2.insert(6);
s2.insert(8);
输出结果:
删除erase
删除
1,删除一个值
2,删除一个位置
3,删除一个区间
三者区别:
值:找到了删,找不到了就不删直接返回
位置:先找到这个是否存在,若存在则删除,不存在了程序会崩溃
删除一个区间(左闭右开):问题:若找不到其中一边的会报错,
查找find
我们将find函数加入到遍历中应用
我们看了查找和删除,那么我们可以替换元素吗?
find一个值,去修改这个值,出来的结果会是错误的,只改变了值,而不满足二叉搜索树的性质了,
在C++11中会报错,解决这个问题,返回值应该为const&
交换swap
我们把s1和s2进行交换
清空clear
clear就是将set中所有元素都删除。
容量
空empty
他经常被作为循环条件或者防御式编程使用
while(!s2.empty())//循环条件
assert(!s2.empty())//防御式编程
大小size和max_size
count
具有特定值的计数元素
搜索容器中的元素等效于val,并返回匹配的数量。
由于集合容器中的所有元素都是唯一的,函数只能返回1(如果找到了元素)或零(否则)。
如果容器的比较对象返回了条件反射(即,不管元素作为参数传递的顺序),那么一个集合的两个元素被认为是等价的。
map
成员函数
map<char, int> m1;//创建一个空map
m1['a'] = 1;//添加
m1['b'] = 2;
m1['c'] = 3;
m1['d'] = 4;
map<char, int> m2(m1);//拷贝构造
map<char, int> m3 = m2;//赋值运算符重载
map<char, int> m4(m1.begin(), m1.end());//初始化为一段区间
在map中下图的用法与set相同,就不在说了
这个是set中没有的,由于map有键值有实值所以他可以用[]来访问实值
查找find:
map<char, int>::iterator it = m1.find('c');
删除erase
1,删除一个位置:
查找到,然后删除
2,删除一个值:
3,删除一段区间
插入
修改map中实值:
注意键值不能修改
关于输出
cout<<*dIt<<endl;//会报错
//解引用不能返回2个值,只能返回一个值
//解决
cout<<(*dIt).first()<<(*dIt).second()<endl;
cout<<dIt->first<<dIt->second()<<endl;
map中的operator[]
我们可以看到它的返回值是带引用的
这说明它不止可以访问,还可以修改
若给的key不存在,那么就会添加
我们可以看到,我们先创建了一个空的map,我们去访问一个不存在的key时,就会创建这个,我们试图访问m1[‘d’],并且把他修改为5
然后遍历一遍,我们可以看到修改成功了。
我们还可以用map来统计数组中每个元素出现的次数
//方法一:
map<string, int> countMap;
string strs[] = { "red", "blue", "white", "red" };
for (size_t i = 0; i < sizeof(strs) / sizeof(strs[0]); ++i)
{
map<string, int>::iterator ret = countMap.find(strs[i]);
if (ret != countMap.end())
{
ret->second++;
}
else
{
//countMap.insert(pair<string, int>(strs[i], 1));
countMap.insert(make_pair(strs[i], 1));
}
}
第一种方法:我们用了insert,我们先找到一个key接着遍历所有的如果在再次出现了让它的second++,如果没有出现就插入。这个方法的缺点就是时间复杂度高,每查找一个就要遍历整个数组
//方法二:
map<string, int> countMap;
string strs[] = { "red", "blue", "white", "red" };
for (size_t i = 0; i < sizeof(strs) / sizeof(strs[0]); ++i)
{
pair<map<string, int>::iterator, bool> ret = countMap.insert(make_pair(strs[i], 1));
{
if (ret.second == false)
{
ret.first->second++;
}
}
}
方法二我们可以定义一个pair类型的变量ret,这个变量包含了map类型,和bool类型,我们每次插入一个他都为true,如果插入失败(之前出现过了就会插入失败),那么我们就ret.first->second++
我们举例ret中的结构是这样的
//方法三:
map<string, int> countMap;
string strs[] = { "red", "blue", "white", "red" };
for (size_t i = 0; i < sizeof(strs) / sizeof(strs[0]); ++i)
{
countMap[strs[i]]++;
}
方法三充分的用了operator[]的性质,实现相对简单
三种的运行出现的结果都是一样的
我们用调试窗口看一下