关联容器
关联容器类型
//按顺序存储
map //关键数组:保存关键字-值对
set //关键字即值,即只保存关键字的容器
multimap //支持同一个键多次出现的map
multiset //支持同一个键多次出现的set
//无序集合
unordered_map //用哈希函数组织的map
unordered_set //用哈希函数组织的set
unordered_multimap //哈希组织的map,关键字可以重复出现
unordered_multiset //哈希组织的set,关键字可以重复出现
使用map
map<string,size_t> word_count;
string word;
while(cin>>word)
++word_count[word];
for(const auto &w : word_count)
cout<<w.first<<w.second<<((w.second>1) ? "times" : "time")<<endl;
使用set
map<string,size_t> word_count;
set<string> exclude = {"the","but","and"};
string word;
while(cin>>word){
if(exclude.find(word) == exclude.end()) //find调用返回一个迭代器,关键字在set中,迭代器指向该关键字,否则,返回尾后迭代器。
++word_count[word];
}
定义关联容器
map<string,size_t> word_count;
set<string> exclude = {};
map<string,string> = {{"",""},{"",""},{"",""}};
//初始化multimap和multiset
vector<int> ivec;
for(vector<int>::size_type i = 0;i!=10;++i){
ivec.push_back(i);
ivec.push_back(i);
}
set<int> iset(ivec.cbegin(),ivec.end());
multiset<int> miset(ivec.cbegin(),ivec.cend());
cout<<ivec.size()<<endl;
cout<<iset.size()<<endl;
cout<<miset.size()<<endl;
关键字类型的要求
- 对于有序容器,关键字类型必须定义元素比较的方法。默认是<。
- 使用关键字类型的比较函数。如不能定义Sales_data的multiset,因为Sales_data没有<运算符。
bool compareIsbn(const Sales_data& lhs,const Sales_data& rhs){
return lhs.isbn() < rhs.isbn();
}
multiset<Sales_data,decltype(compareIsbn)*> bookstore(compareIsbn);//第二个实际是指针。
pair类型 头文件utility中
pair<string,string> anon;
pair<string,size_t> word_count;
pair<string,vector<int>> line;
pair<T1, T2> p; //p是一个pair,两个类型分别是T1和T2的成员都进行了值初始化。
pair<T1, T2> p(v1, v2); //first和second分别用v1和v2进行初始化。
pair<T1, T2>p = {v1, v2}; //等价于`p(v1, v2)
make_pair(v1, v2); //pair的类型从v1和v2的类型推断出来。
p.first //返回p的名为first的数据成员。
p.second //返回p的名为second的数据成员。
p1 relop p2 //运算关系符按字典序定义。
p1 == p2 //必须两对元素两两相等
p1 != p2 //同上
//创建pair对象的函数
pair<string,int> process(vector<string> &v){
if(!v.empty())
return {v.back(),v.back().size()};
else
return pair<string,int>(); //空pair
}
return pair<string,int>(v.back(),v.back().size());
return make_pair(v.back(),v.back().size());
关联容器操作
key_type //此容器类型的关键字类型
mapped_type //每个关键字关联的类型,只适用于map
value_type //对于map,是pair<const key_type, mapped_type>; 对于set,和key_type相同。
//set类型key_type和value_type一样
map<string,int>::value_type v3; //v3是一个pair<const string,int>
关联容器迭代器
- 解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用,对map而言,value_type是一个pair类型,first成员保存const的关键字,second成员保存值。
auto map_it = word_count.begin();
//*map_it是指向一个pair<const string,size_t>的引用
cout<<map_it->first;
cout<<map_it->second;
++map_it->second;
//set的迭代器是const的
set<int> iset = {};
set<int>::iterator set_it = iset.begin();
if(set_it!=iset.end()){
*set_it = 43; //wrong
cout<<*set_it<<endl;
}
//遍历关联容器
auto map_it = word_count.cbegin();
while(map_it != word_count.cend()){
cout<<map_it->first<<map_it->second<<endl;
++map_it;//迭代器按关键字升序遍历元素
}
添加元素
c.insert(v)
c.emplace(args)
//v是value_type类型的对象;args用来构造一个元素。对于map和set,只有元素的关键字不存在c中才插入或构造元素。函数返回一个pair,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的bool值。对于multimap和multiset则会插入范围中的每个元素。
c.insert(b, e)
c.insert(il)
//b和e是迭代器,表示一个c::value_type类型值的范围;il是这种值的花括号列表。函数返回void。对于 map和set,只插入关键字不在c中的元素。
c.insert(p, v)
c.emplace(p, args)
//类似insert(v),但将迭代器p作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素。
set.insert(ivec.cbegin(),ivec.cend());
word_count.insert({word,1});
word_count.insert(make_pair(word,1));
word_count.insert(pair<string,size_t>(word,1));
word_count.insert(map<string,size_t>::value_type(word,1));
检测insert返回值
- 对不包含重复关键字的容器,添加单一元素的insert和emplace返回一个pair。first成员是一个迭代器,指向具有给定关键字的元素,second成员是bool值,指出插入是否成功。
map<string,size_t> word_count;
string word;
while(cin>>word){
auto ret = word_count.insert({word,1});
if(!ret.second)
++ret.first->second;
}
//ret.first->解引用此迭代器,提取map中的元素,元素也是一个pair
pair<map<string,size_t>::iterator,bool> ret = word_count.insert(make_pair(word,1));
//multimap
multimap<string,string> authors;
authors.insert({"dd","cc"});
authors.insert({"dd","bb"});
//返回一个指向新元素的迭代器且无须返回一个bool值。
删除元素
if(word_count.erase(removal_word))
cout<<removal_word<<endl;
else
cout<<removal_word<<endl;
//对于保存不重复关键字的容器,erase返回值总是0或1,返回值为0,表明想要删除的元素并不在容器中,对允许重复关键字的容器,删除元素的数量可能大于1。
auto cnt = authors.erase("dd"); //cnt = 2
c.erase(k) //从c中删除每个关键字为k的元素。返回一个size_type值,指出删除的元素的数量。
c.erase(p) //从c中删除迭代器p指定的元素。p必须指向c中一个真实元素,不能等于c.end()。返回一个指向p之后元素的迭代器,若p指向c中的尾元素,则返回c.end()
c.erase(b, e) //删除迭代器对b和e所表示范围中的元素。返回e。
map的下标操作
map<string,size_t> word_count;
word_count["Anna"] = 1;
c[k] //返回关键字为k的元素;如果k不在c中,添加一个关键字为k的元素,对其值初始化。
c.at(k) //访问关键字为k的元素,带参数检查;若k不存在在c中,抛出一个out_of_range异常。
//下标操作获得mapped_type对象,返回的是一个左值,可读可写
//而解引用map迭代器得到value_type对象
cout<<word_count["Anna"]; //1
++word_count["Anna"];
cout<<word_count["Anna"];//2
访问元素
c.find(k) //返回一个迭代器,指向第一个关键字为k的元素,若k不在容器中,则返回尾后迭代器
c.count(k) //返回关键字等于k的元素的数量。对于不允许重复关键字的容器,返回值永远是0或1。
c.lower_bound(k) //返回一个迭代器,指向第一个关键字不小于k的元素。
c.upper_bound(k) //返回一个迭代器,指向第一个关键字大于k的元素。
c.equal_range(k) //返回一个迭代器pair,表示关键字等于k的元素的范围。若k不存在,pair的两个成员均等于c.end()。
//对map使用find替代下标操作,下标操作会插入新元素
if(word_count.find("foobar") == word_count.end()){
cout<<"foobar is not in the map"<<endl;
}
//在multimap或multiset中查找元素
string search_item("name");
auto entries = authors.count(search_item);
auto iter = authors.find(search_item);
while(entries){
cout<<iter->second<<endl;
++iter;
--entries;
}
//一种不同的面向迭代器的解决方法
for(auto beg=authors.lower_bound(search_item),end=authors.upper_bound(search_item);beg!=end;++beg)
cout<<beg->second<<endl;
//如果lower_bound和upper_bound返回相同的迭代器,则给定关键字不在容器中。
//equal_range函数
for(auto pos=authors.equal_range(search_item);pos.first!=pos.second;++pos.first){
cout<<pos.first->second<<endl;
}
//pos.first等价于beg
//pos.second等价于end
单词转换程序
where r u
y dont u send me a pic
k thk l8r
转换为
where are you
why dont you send me a picture
okay? thanks! later
建立转换映射
map<string,string> buildMap(ifstream& map_file){
map<string,string> trans_map;
string key;
string value;
while(map_file>>key && getline(map_file,value)){
if(value.size() > 1)
trans_map[key] = value.substr[1]; //getline不会跳过前导空格
else
throw runtime_error("no rule for" + key);
}
return trans_map;
}
生成转换文本
const string& transform(const string& s,const map<string,string>& m){
auto map_it = m.find(s);
if(map_it != m.cend())
return map_it->second;
else
return s;
}
单词转换程序
void word_transform(ifstream& map_file,ifstream& input){
auto trans_map = buildMap(map_file);
string text;
while(getline(input,text)){
isstringstream stream(text);
string word;
bool firstword = true;
while(stream>>word){
if(firstword)
firstword = false;
else
cout<<" ";
cout<<transform(word,trans_map);
}
cout<<endl;
}
}
无序容器
- 有序容器使用比较运算符来组织元素;无序容器使用哈希函数和关键字类型的==运算符。
- 理论上哈希技术可以获得更好的性能。
- 无序容器在存储上组织为一组桶(bucket),每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。
unordered_map<string,size_t> word_count;
stirng word;
while(cin>>word)
++word_count[word];
for(const auto& w : word_count)
cout<<w.first<<w.second<<endl;
无序容器管理操作:
桶接口
c.bucket_count() //正在使用的桶的数目
c.max_bucket_count() //容器能容纳的最多的桶的数目
c.bucket_size(n) //第n个桶中有多少个元素
c.bucket(k) //关键字为k的元素在哪个桶中
桶迭代
local_iterator //可以用来访问桶中元素的迭代器类型
const_local_iterator //桶迭代器的const版本
c.begin(n),c.end(n) //桶n的首元素迭代器
c.cbegin(n),c.cend(n) //与前两个函数类似,但返回const_local_iterator。
哈希策略
c.load_factor() //每个桶的平均元素数量,返回float值。
c.max_load_factor() //c试图维护的平均比桶大小,返回float值。c会在需要时添加新的桶,以使得load_factor<=max_load_factor
c.rehash(n) //重组存储,使得bucket_count>=n,且bucket_count>size/max_load_factor
c.reverse(n) //重组存储,使得c可以保存n个元素且不必rehash。
size_t hasher(const Sales_data& sd){
return hash<string>()(sd.isbn());
}
//hasher函数使用标准库hash类型对象计算ISBN成员的哈希值,该hash类型建立在string类型之上
bool eqOp(const Sales_data& lhs,const Sales_data& rhs){
return lhs.isbn() == rhs.isbn();
}
//eqOp函数通过比较ISBN号来比较两个Sales_data.
//我们用这些函数来定义一个unordered_multiset
using SD_multiset = unordered_multiset<Sales_data,decltype(hasher)*,decltype(eqOp)*>;
SD_multiset bookstore(42,hasher,eqOp);
//如果类定义了==运算符,则可以只重载哈希函数
//使用FooHash生成哈希值;Foo必须有==运算符
unordered_set<Foo,decltype(FooHash)*>fooSet(10,FooHash);