我们前面接触的vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是
什么是键值对?
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
pair就是C++内置的一个键值对。
first就是Key,second就是value。
它的第二个构造函数就保证了它只要first之间是可以相互赋值就就可以,因为它的构造函数又套了一个模板参数,所以如果u,v和T1,T2相等就是拷贝构造函数,如果不相等就是构造函数,所以它既是拷贝构造也是构造。
我们可以看到pair
但是我们一般不直接写pair来构造pair,我们都用make_pair.
make_pair
make_pair可以根据我们传递的类型帮我们返回一个对应的pair。
树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。
插入
我们可以看到这里的插入是一个val_type,val_type是什么呢?
我们可以看到val_type就是T。
void func()
{
set<int> s;
s.insert(6);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
s.insert(0);
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
我们可以看到insert的返回值是一个pair,pair的first是这个位置的迭代器,second是是否插入成功。
删除
删除可以删除一个迭代器的位置,也可以直接删除一个val_type,也可以直接删除一个迭代器区间。
void func1()
{
set<int> s;
s.insert(6);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
s.insert(0);
s.erase(0);
set<int>::iterator it = s.begin();
s.erase(it);
it = s.begin();
while (it != s.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
查找
查找是返回这个值的迭代器。可以配合删除使用。如果找不到会返回end()的位置。
lower_bound和upper_bound
lower_bound会返回大于等于val的位置的迭代器,而upper_bound会返回大于val的位置迭代器。
这两个函数一般是配合使用的,我们可以通过这两个函数来遍历,某一段区间内的值,或者删除某一段区间的值。
void func2()
{
set<int> s;
for (size_t i = 0; i < 10; i++)
{
s.insert(i * 10);
}
set<int>::iterator it1, it2;
it1 = s.lower_bound(30);
it2 = s.upper_bound(60);
while (it1 != it2)
{
cout << *it1 << " ";
it1++;
}
cout << endl;
}
equal会返回该val位置的迭代器,和大于该val的迭代器。并且把他们放到一个pair中,first是val的位置的迭代器,second是大于val位置的迭代器。
void func3()
{
set<int> s;
for (size_t i = 0; i < 10; i++)
{
s.insert(i * 10);
}
auto m = s.equal_range(20);
cout << *m.first << endl; // 20
cout << *m.second<<endl; // 30
}
multiset和set一样,只不过multiset允许键值相同,而set是不允许键值相同的。只不过multiset在删除是,会把那个键值的所有值都删了,并不会只删除一个。
总结:
插入
map就是一个K,V模型,里面存的是pair,所以我们要插入就是要插入一个pair。
如果插入成功会返回那个位置的迭代器,如果那个值已经存在了,就会返回那个位置的迭代器,所以insert还充当的有find的功能。后面的[]重载也是主要有insert实现的。
void func()
{
map<string, string> mymap;
mymap.insert(make_pair("sort", "排序"));
mymap.insert(make_pair("left", "左边"));
mymap.insert(make_pair("right", "右边"));
map<string, string>::iterator it = mymap.begin();
while (it != mymap.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
}
然后删除什么的和set的用法都是一样的,只是map重载了[]运算符,我们主要说一下这个。
我们会看到insert就是调用了insert这个函数,那么为什么可以这样呢?
因为insert如果不存在就会插入成功并且返回该位置的迭代器,如果这个值已经存在,就会插入失败并且也会返回这个位置的迭代器,所以我们不用管是不是插入成功了,都能够得到这个位置的迭代器。
比如统计次数我们就可以这样写:
void func1()
{
map<string, int> countMap;
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
for (auto e : arr)
{
countMap[e]++;
}
map<string, int>::iterator it = countMap.begin();
while (it != countMap.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
}
multimap也是允许键值冗余,所以它就没办法重载[]。其他的和map都一样。
总结:
那么今天的分享就到这里了,有什么不懂得可以私信博主,或者添加博主的微信,欢迎交流。