目录
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;
}