练习11.1:描述map 和 vector 的不同。

map是关联容器,vector是顺序容器,关联容器与值无关,vector则与值密切相关

练习11.2:分别给出最适合使用 list、vector、deque、map以及set的例子。

list链表
vector动态数组
deque队列
map映射
set集合

练习11.3:编写你自己的单词计数程序。

#include <iostream>
#include <map> void words_count()
{
std::map<std::string, std::size_t> m;
std::string word;
while (std::cin >> word)
{
++m[word];
}
} int main()
{
words_count();
return ;
}

练习11.4:扩展你的程序,忽略大小写和标点。例如,"example."、"example,"和"Example"应该递增相同的计数器。

#include <iostream>
#include <map> void words_count()
{
std::map<std::string, std::size_t> m;
std::string word;
while (std::cin >> word)
{
for (auto& ch : word)
{
ch = tolower(ch);
}
if (ispunct(*--word.end()))
{
word = word.substr(, word.size() - );
} ++m[word]; for (const auto &c : m)
{
std::cout << c.first << " occurs " << c.second << " times." << std::endl;
}
}
} int main()
{
words_count();
return ;
}

练习11.5:解释map和set的区别。你如何选择使用哪个?

map是键-值对的银蛇集合,而set是单纯的键集合。根据实际情况选择~~~

练习11.6:解释set和list的区别。你如何选择使用哪个?

set是单纯的键集合,list是链式值集合。根据实际情况选择~~~

练习11.7:定义一个map,关键字是家庭的姓,值是一个vector,保存家中孩子(们)的名。编写代码,实现添加新的家庭以及向已有家庭中添加新的孩子。

#include <iostream>
#include <vector>
#include <map> void fun()
{
std::map<std::string, std::vector<std::string>> m;
std::string family, child;
std::cout << "input family name:";
while (std::cin >> family)
{
std::cout << "input child name:";
while (std::cin >> child)
{
m[family].push_back(child);
std::cout << "continue child?(Y/N):";
char c = 'Y';
std::cin >> c;
if (c == 'N' || c == 'n')
{
break;
}
}
std::cout << "continue family?(Y/N):";
char c = 'Y';
std::cin >> c;
if (c == 'N' || c == 'n')
{
break;
}
} for (const auto &c : m)
{
std::cout << "family: " << c.first << std::endl;
std::cout << "child: ";
for (auto i : c.second)
{
std::cout << i << '\t';
}
std::cout << std::endl;
}
} int main()
{
fun();
return ;
}

练习11.8:编写一个程序,在一个vector而不是一个set中保存不重复的单词。使用set的优点是什么?

#include <iostream>
#include <algorithm>
#include <vector>
#include <map> void fun()
{
std::vector<std::string> v;
std::string word;
while (std::cin >> word)
{
if (std::find(v.begin(), v.end(), word) == v.end())
{
v.push_back(word);
}
}
for (auto c : v)
{
std::cout << c << '\t';
}
} int main()
{
fun();
std::cout << std::endl;
return ;
}

优点是无需考虑键值重复问题。

练习11.9:定义一个map,将单词与一个行号的list关联,list中保存的是单词所出现的行号。

std::map<std::string, std::list<unsigned>> map;

练习11.10:可以定义一个vector<int>::iterator 到 int 的map吗?list<int>::iterator 到 int 的map呢?对于两种情况,如果不能,解释为什么。

可以定义 vector<int>::iterator 到 int 的map,不能定义 list<int>::iterator 到 int 的map。因为map的关键字类型要求 < 操作,list 的迭代器不支持比较运算。

练习11.11:不使用decltype 重新定义 bookstore。

multiset<Sales_data,bool (*)compareIsbn(const Sales_data &,const Sales_data &)> bookstore(compareIsbn);

练习11.12:编写程序,读入string和int的序列,将每个string和int存入一个pair中,pair保存在一个vector中。

#include <iostream>
#include <utility>
#include <vector> int main()
{
std::string word;
int i;
std::vector<std::pair<std::string, int>> v;
std::cout << "input string:" << std::flush;
while (std::cin >> word)
{
std::cout << "input number:" << std::flush;
std::cin >> i;
v.push_back(make_pair(word, i));
std::cout << "continue(Y/N):" << std::flush;
char c = 'Y';
std::cin >> c;
if (c == 'N' || c == 'n')
{
break;
}
std::cout << "input string:" << std::flush;
}
for (const auto &i : v)
{
std::cout << i.first << '\t' << i.second << std::endl;
}
return ;
}

练习11.13:在上一题的程序中,至少有三种创建pair的方法。编写此程序的三个版本,分别采用不同的方法创建pair。解释你认为哪种形式最易于编写和理解,为什么?

// #1
v.push_back({word, i});
// #2
v.push_back(make_pair(word, i));
// #3
v.push_back(std::pair<std::string, int>(word, i));

第二种易于理解,第一种简洁,第三种则易读

练习11.14:扩展你在11.2.1节练习(第378页)中编写的孩子姓到名的map,添加一个pair的vector,保存孩子的名和生日。

#include <iostream>
#include <vector>
#include <map> void fun()
{
std::map<std::string, std::vector<std::string>> m;
std::vector<std::pair<std::string, std::string>> v;
std::string family, child, birth;
std::cout << "input family name:";
while (std::cin >> family)
{
std::cout << "input child name:";
while (std::cin >> child)
{
m[family].push_back(child);
std::cout << "input child birthday:";
std::cin >> birth;
v.push_back({child, birth});
std::cout << "continue child?(Y/N):";
char c = 'Y';
std::cin >> c;
if (c == 'N' || c == 'n')
{
break;
} }
std::cout << "continue family?(Y/N):";
char c = 'Y';
std::cin >> c;
if (c == 'N' || c == 'n')
{
break;
}
} for (const auto &c : m)
{
std::cout << "family: " << c.first << std::endl;
std::cout << "child: ";
for (auto i : c.second)
{
std::cout << i << '\t';
}
std::cout << std::endl;
}
} int main()
{
fun();
return ;
}

练习11.15:对一个int到vector<int>的map,其mapped_type、key_type和value_type分别是什么?

mapped_type是vector<int>,key_type是int,value_type是pair<int,vector<int>>

练习11.16:使用一个map迭代器编写一个表达式,将一个值赋予一个元素。

std::map<int, char> m;
std::map<int, char>::iterator m_it = m.begin();
m_it->second = 'c';

练习11.17:假定 c 是一个string的multiset,v是一个string的vector,解释下面的调用。指出每个调用是否合法:

copy(v.begin(), v.end(), inserter(c, c.end()));
copy(v.begin(), v.end(), back_inserter(c));
copy(c.begin(), c.end(), inserter(v, v.end()));
copy(c.begin(), c.end(), back_inserter(v));

第二个错误,mutiset没有push_back操作

练习11.18:写出第382页循环中map_it的类型,不要使用auto或decltype。

std::map<std::string, size_t>::iterator

练习11.19:定义一个变量,通过对11.2.2节(第378页)中的名为 bookstore 的multiset调用begin()来初始化这个变量。写出变量的类型,不要使用auto或decltype。

multiset<Sales_data>::iterator mset=bookstore.begin();

练习11.20:重写11.1节练习(第376页)的单词计数程序,使用insert代替下标操作。你认为哪个程序更容易编写和阅读?解释原因。

#include <iostream>
#include <map> void words_count()
{
std::map<std::string, std::size_t> m;
std::string word;
while (std::cin >> word)
{
auto ret = m.insert({word, });
if (!ret.second)
{
++ret.first->second;
}
}
} int main()
{
words_count();
return ;
}

第一种更容易阅读和书写。

练习11.21:假定word_count是一个string到size_t的map,word是一个string,解释下面循环的作用:

while (cin >> word)
++word_count.insert({word, }).first->second;

该语句是一个链式表达式的语句,首先插入一个pair,然后返回一个pair,返回的pair,第一个是指向map的元素的迭代器,然后对该迭代器解引用,获得所指元素关联的值,最后递增该值。

练习11.22:给定一个map<string,vector<int>>,对此容器的插入一个元素的insert版本,写出其参数类型和返回类型。

参数:pair<string, vector<int>>
返回类型:pair<map<string, vector<int>>::iterator,bool>

练习11.23:11.2.1节练习(第378页)中的map以孩子的姓为关键字,保存他们的名的vector,用multimap重写此map。

#include <iostream>
#include <vector>
#include <map> void fun()
{
std::multimap<std::string, std::vector<std::string>> m;
std::string family, child;
std::cout << "input family name:";
while (std::cin >> family)
{
std::cout << "input child name:";
while (std::cin >> child)
{
std::vector<std::string> v;
v.push_back(child);
m.insert({family, v});
std::cout << "continue child?(Y/N):";
char c = 'Y';
std::cin >> c;
if (c == 'N' || c == 'n')
{
break;
}
}
std::cout << "continue family?(Y/N):";
char c = 'Y';
std::cin >> c;
if (c == 'N' || c == 'n')
{
break;
}
} for (const auto &c : m)
{
std::cout << "family: " << c.first << std::endl;
std::cout << "child: ";
for (auto i : c.second)
{
std::cout << i << '\t';
}
std::cout << std::endl;
}
} int main()
{
fun();
return ;
}

练习11.24:下面的程序完成什么功能?

map<int, int> m;
m[] = ;

添加一个关键词到map中,如果该关键词已存在,则重新赋值。

练习11.25:对比下面的程序与上一题程序

vector<int> v;
v[] = ;

错误,不能对空vector直接赋值。

练习11.26:可以用什么类型来对一个map进行下标操作?下标运算符返回的类型是什么?请给出一个具体例子———即,定义一个map,然后写出一个可以用来对map进行下标操作的类型以及下标运算符将会返会的类型。

std::map<int, char> m = { , '' };
int index; //索引
char returnType; //返回类型

练习11.27:对于什么问题你会使用count来解决?什么时候你又会选择find呢?

需要知道数量时使用count,需要知道第一个元素位置时使用find。

练习11.28:对一个string到int的vector的map,定义并初始化一个变量来保存在其上调用find所返回的结果。

map<string,int>::iterator ret=map.end();

练习11.29:如果给定的关键字不在容器中,upper_bound、lower_bound 和 equal_range 分别会返回什么?

如果给定的关键字不在容器中,则 lower_bound和 upper_bound 会返回相等的迭代器,指向一个不影响排序的关键字插入位置。而equal_range 会返回一个 pair,pair 中的两个迭代器都指向关键字可以插入的位置。

练习11.30:对于本节最后一个程序中的输出表达式,解释运算对象pos.first->second的含义。

pos是指向map中的元素的迭代器pair,首先获取第first元素迭代器,然后解引用得到pair元素,再对pair元素解引用获取元素相关联的值。

练习11.31:编写程序,定义一个作者及其作品的multimap。使用find在multimap中查找一个元素并用erase删除它。确保你的程序在元素不在map中时也能正常运行。

#include <iostream>
#include <vector>
#include <map> void fun()
{
std::multimap<std::string, std::string> m;
std::string author, writing;
std::cout << "input author name:";
while (std::cin >> author)
{
if (author == "N" || author == "n")
{
break;
}
std::cout << "input writing name:";
std::cin >> writing;
m.insert({author, writing});
std::cout << "input author name(N for break):"; } std::cout << std::endl << std::endl;
for (const auto &c : m)
{
std::cout << "author: " << c.first << std::flush;
std::cout << " writing: " << c.second << std::endl;;
} std::cout << "input author name that you want to delete: ";
std::string del;
std::cin >> del;
if (m.find(del) != m.end())
{
m.erase(m.find(del));
}
std::cout << std::endl << std::endl;
for (const auto &c : m)
{
std::cout << "author: " << c.first << std::endl;
std::cout << "writing: " << c.second << std::endl;;
}
std::cout << std::endl; } int main()
{
fun();
return ;
}

练习11.32:使用上一题定义的multimap编写一个程序,按字典序打印作者列表和他们的作品。

上一题已按字典序打印,参见上一题。

练习11.33:实现你自己版本的单词转换程序。

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map> using namespace std; 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() > )
trans_map[key] = value.substr();
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))
{
istringstream stream(text);
string word;
bool firstword = true;
while (stream >> word)
{
if (firstword)
firstword = false;
else
cout << " ";
cout << transform(word, trans_map);
}
cout << endl;
}
} int main()
{
return ;
}

练习11.34:如果你将transform函数中的find替换为下标运算符,会发生什么情况?

如果使用下标运算符,当关键字未在容器中时,会导致容器中新添加一个元素。

练习11.35:在buildMap中,如果进行如下改写,会有什么效果?
trans_map[key] = value.substr(1);
改为trans_map.insert({key, value.substr(1)});

因为下标运算符没有就插入,有就是赋值,而insert没有时才插入,有时不做任何操作,因此当一个转换规则即key多次出现的时候,下标运算符会用最后一次添加的值来赋值覆盖,而用insert则是第一次添加的值。

练习11.36:我们的程序并没检查输入文件的合法性。特别是,它假定转换规则文件中的规则都是有意义的。如果文件中的某一行包含一个关键字、一个空格,然后就结束了,会发生什么?预测程序的行为并进行验证,再与你的程序进行比较。

terminate called after throwing an instance of 'std::runtime_error'
what(): no rule for k
已放弃 (核心已转储)

从程序运行结果来看,会抛出异常。

练习11.37:一个无序容器与其有序版本相比有何优势?有序版本有何优势?

无序性能更高,有序则可以保持顺序。

练习11.38:用 unordered_map 重写单词计数程序(参见11.1节,第375页)和单词转换程序(参见11.3.6节,第391页)。

C++ Primer 5th 第11章 关联容器的更多相关文章

  1. 【c++ Prime 学习笔记】第11章 关联容器

    关联容器的元素按照关键字来保存和访问,而顺序容器的元素是按照在容器中的位置来保存和访问 关联容器支持高效的关键字查找和访问 2种关联容器: map中的元素是关键字-值对(key-value对),关键字 ...

  2. [C++ Primer] : 第11章: 关联容器

    目录 使用关联容器 关联容器概述 关联容器操作 无序容器 使用关联容器 关联容器与顺序容器有着根本的不同: 关联容器中的元素是按关键字来保存和访问的, 按顺序容器中的元素是按它们在容器中的位置来顺序保 ...

  3. 《C++ Primer》笔记 第11章 关联容器

    关联容器类型 解释 按关键字有序保存元素 -- map 关联数组:保存关键字-值对 set 关键字即值,即只保存关键字的容器 multimap 关键字可重复出现的map multiset 关键字可重复 ...

  4. C++ Primer 5th 第9章 顺序容器

    练习9.1:对于下面的程序任务,vector.deque和list哪种容器最为适合?解释你的选择的理由.如果没有哪一种容器优于其他容器,也请解释理由.(a) 读取固定数量的单词,将它们按字典序插入到容 ...

  5. C++ primer 11章关联容器

    map set multimap (关键字可重复出现) multiset 无序 unordered_map  (用哈希函数组织的map) unordered_set unordered_multima ...

  6. C++ Primer 读书笔记:第10章 关联容器

    第10章 关联容器 引: map set multimap multiset 1.pair类型 pair<string, int> anon anon.first, anon.second ...

  7. C++ Primer : 第十一章 : 关联容器之关联容器的迭代器和操作

    关联容器的操作 除了和顺序容器定义的类型之外,关联容器还定义了一下几种类型: 关联容器额外的类型别名  key_type    此容器类型的关键字类型 mapped_type  每个关键字关联的类型, ...

  8. C++ Primer : 第十一章 : 关联容器之概述、有序关联容器关键字要求和pair类型

    标准库定义了两种主要的关联容器:map和set map中的元素时一些关键字-值(key-value)对,关键字起到索引的作用,值则表示与索引相关的数据.set中每个元素只包含一个关键字,可以完成高效的 ...

  9. C++ Primer 5th 第1章 开始

    *****代码在Ubuntu g++ 5.31 / clang++ 3.8(C++11)下编写调试***** 每个C++程序必须有一个main( )函数,main( )函数的返回值也必须是int类型, ...

随机推荐

  1. Farm Irrigation(并查集)

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...

  2. Round Numbers (排列组合)

    Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7558   Accepted: 2596 Description The c ...

  3. Cash Machine(多重背包)

    Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 24067   Accepted: 8414 Description A Ba ...

  4. 【转】 Android的NDK开发(1)————Android JNI简介与调用流程

    原文网址:http://blog.csdn.net/conowen/article/details/7521340 ****************************************** ...

  5. OC中的野指针(僵尸指针)

    涉及到内存管理问题的都是类类型的变量,而在OC中我们操纵这些对象都是通过操纵指向他们的指针来完成的,一致很多时候会忽略指针存在.比如定义UIView * view = [[UIView alloc]i ...

  6. 线段树(维护最大值):HDU Billboard

    Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  7. android实现图片识别的几种方法

    实现android图像识别的几种方法 点击这里下载第一种代码 最近完成了毕业设计,论文名为基于图像识别的移动人口管理系统.编写过程中学到了几种图像识别的技术,先写下来与大家分享. 第一种,直接使用免费 ...

  8. 约瑟夫环问题-Java数组解决

    约瑟夫环问题说的是,n个人围成一圈,从第k个人开始沿着一个方向报数,报到第m个人时,第m个人出列,从紧挨着的下一个人(未出列)开始,求整个环中人的出列顺序.下面是我用java实现的解决方法. clas ...

  9. 《A First Course in Mathematical Modeling》-chaper1-差分方程建模

    从今天开始笔者将通过这个专栏可是对“数学建模”的学习.其实对于“数学建模”自身的内涵或者意义并不需要太多的阐释,下图简洁明了的阐释了数学建模的意义. 其实数学建模本身可以看成换一种角度去解读数学,将我 ...

  10. 区别assign VS weak,__block VS __weak

    在objective-c中,类中的全局变量经常使用如下的方式申明. @property(nonatomic(1),strong(2))UIImageView *imageView; 其中的1,2处是对 ...