C++ Primer 【第四版】第十章 关联容器
2014-08-12 09:34
302 查看
1.编写程序读入一些列string和int型数据,将每一组存储在一个pair对象中,然后将这些pair对象存储在vector容器里。
//11.16_10.1_pair.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<utility>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
stringstr;
int iVal;
cout<< "\tInput some pair contents(pair<string, int > ) (^Z to end):\n";
vector<pair<string,int> >
pairVec;
while( cin >>
str>> iVal )
pairVec.push_back(
pair<string,
int> ( str, iVal));
cout<< "\n\t The content of pairVecis:\n" ;
for ( vector<
pair<string,
int>>::iterator
it= pairVec.begin();it !=
pairVec.end(); ++it )
{
cout<< it->first
<< " " <<
it->second<< endl;
}
system("pause");
return0;
}
2.在前一题中,至少可使用三种方法创建pair对象。编写三个版本的程序,分别采用不同的方法来创建pair对象。你认为哪一种方法更易于编写和理解,为什么?
stringstr;
int iVal;
vector<pair<string,int> >
pairVec;
pair<string,
int> newPair;
while( cin >> str >> iVal )
{
// first method
pairVec.push_back(pair< string, int > ( str, iVal) );
//the second method
newPair = make_pair( str,iVal );
pairVec.push_back( newPair );
}
// thirdmethod
while (cin >>
newPair.first >>
newPair.second )
{
pairVec.push_back(
newPair);
}
第二种方法更好一些,更容易阅读和理解。因为它调用了make_pair函数,可以明确地表明确实生成了pair对象这一行为。
3.描述关联容器和顺序容器的差别。
两者的本质差别在于:关联容器通过键 key
存储和读取元素,
而顺序容器则通过元素在容器中的位置顺序存储和访问元素。
4.举例说明list、vector、deque、map以及set类型分别使用的情况。
list类型适用于需要在容器的中间位置插入和删除元素的情况,如以无序的方式读入一系列学生的数据;
vector类型适用于需要随机访问元素的情况。如:在学号为1…n的学生中,访问第
x 学号的学生的信息。
deque类型适用于在容器的尾部或首部有插入和删除元素情况。如:对服务窗口先来先服务的情况。
map适用于需要
key – value 对的集合的情况。如:字典电和话簿的建立和使用。
set类型适用于使用键集合的情况。例如,黑名单的建立和使用。
5.定义一个Map对象,将单词与一个list对象关联起来,该list对象存储对应的单词可能出现的行号。
map< string, list<int> > wordLines;
6.可否定义一个map对象以vector<int>::interator为键关联int型对象?如果以list<int>::iterator关联int型对象呢?或者,以pair<int,
string>关联int?对于每种情况,如果允许,请解释其原因。
可以定义一个map对象以vector<int>::iterator
和 pair< int,string >
为键关联int
型对象。
不能定义第二种情况,因为键类型必须支持 <
操作,而list容器的迭代器类型不支持<操作。
pair<int, string>关联int可以。
7.对于以int型对象为索引关联vector<int>型对象的map容器,它的mapped_type。key_type和value_type分别是什么?
分别是: vector<int> , int
和 pair< const int, vector<int> >
。
8.编写一个表达式,使用map的迭代器给其元素赋值。
map< string, int > m;
map< string, int >::iterator map_it = m.begin();
map_it->second = val;
// 只能对map的值成员元素赋值。不能对键进行赋值。
9.编写程序统计并输出所读入的单词出现的次数。
//11.16_10.9_wordcount.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
map<string,
int > wordCount;
stringword;
while (cin >>
word)
{
++wordCount[word ];
cout<< " The word '" <<
word << "'appears for "
<< wordCount[ word ]<<
" times." << endl;
}
system("pause");
return0;
}
10.解释下面程序的功能:
map<int,int> m;
m[0]= 1;
比较上一程序和下面程序的行为
vector<int>v;
v[0]= 1;
首先创建一个空的map容器m,然后再m中增加一个键为0的元素,并将其值赋值为1,
第二段程序将出现运行时错误,因为v为空的vector对象,其中下标为0的元素不存在。对于vector容器,不能对尚不存在的元素直接赋值,只能使用push_back、insert等函数增加元素。
11.哪些类型可用做map容器对象的下标?下标操作符返回的又是什么类型?给出一个具体例子说明,即定义一个map对象,指出哪些类型可用作其下标,以及下标操作符返回的类型。
可用作map容器的对象的下标必须是支持
< 操作符的类型。
下标操作符的返回类型为map容器中定义的mapped_type类型。
如 map< string, int >wordCount;
可用于其下标的类型为string类型以及C风格字符串类型,下标的操作符返回int类型。
12.重写10.3.4节习题的单词统计程序,要求使用insert函数代替下标运算。你认为哪个程序更容易编写和阅读?请解释原因。
//11.16_10.12_wordCount_insert.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
cout<< " Input some words ( ctrl + z to end):" <<
endl;
map<string,
int > wordCount;
stringword;
while (cin >>
word)
{
pair<map<
string,int >::iterator,
bool > ret=
wordCount.insert(
make_pair( word, 1 ) );
if( !ret.second)
++ret.first->second;
}
for ( map<
string, int >::iterator
it = wordCount.begin();
it != wordCount.end(); ++it )
{
cout<< " The word '" << (*it).first <<
"' appears for " << (*it).second <<" times. " <<
endl;
}
system("pause");
return0;
}
13.假设有map<string,vector<int> >类型,指出在该容器中插入一个元素的insert函数应具有的参数类型和返回值类型。
参数类型:pair<const string, vector<int> >
返回值类型:pair<map< string, vector<int> >::iterator , bool >
14.map容器的count和find运算有何区别?
前者返回map容器中给定的键K的出现次数,且返回值只能是0或者1,因为map只允许一个键对应一个实例。
后者返回给定键K元素索引时
指向元素的迭代器,若不存在此K值,则返回超出末端迭代器。
15.你认为count适合用于解决哪一类问题?而find呢?
count适合用于判断map容器中某键是否存在,find适合用于在map容器中查找指定键对应元素。
16.定义并初始化一个变量,原来存储调用键为string,值为vector<int>的map对象的find函数的返回结果。
map< string, vector<int> >::iterator it = xMap.find( K);
17.上述转换程序使用了find函数来查找单词:
map<string,string>::const_iterator map_it = trans_map.find(word);
你认为这个程序为什么要使用find函数?如果使用下标操作符又会怎样?
使用find函数,是为了当文本中出现的单词word是要转换的单词时,获取该元素的迭代器,以便获取对应的转换后的单词。
如果使用下标,则必须先通过使用count函数来判断元素是否存在,否则,当元素不存在时,会创建新的元素并插入到容器中从而导致单词转换结果不是预期效果
18.定义一个map对象,其元素的键是家族姓氏,而值则是存储该家族孩子名字的vector对象。为这个map容器输入至少六个条目。通过基于家族姓氏的查询检测你的程序,查询应输出该家族所有孩子的名字。
//11..16_10.18_map_vector_children'sName.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
map<string,
vector<string> >
children;
stringfamName,
childName;
do
{
cout<< " Input
families' name( ctrl + z to end ): "<< endl;
cin>> famName;
if( !cin )
break;
vector<string>
chd;
pair<map<
string,vector<string>>::iterator,
bool> ret =
children.insert(
make_pair( famName,chd ) );
if( !ret.second)
{
cout << "Already exist the family name: " <<
famName<< endl;
continue;
}
cout<< "\n\tInput children's name ( ctrl + zto end ): " <<
endl;
while( cin >>
childName)
ret.first->second.push_back(
childName );
cin.clear();
} while (
cin);
cin.clear();
cout<< "\n\tInput a family name to search:" <<
endl;
cin>> famName;
map<string,
vector<string> >::iteratoriter =
children.find(
famName );
if ( iter ==
children.end() )
cout<< "\n\t Sorry, there is not this familyname: " <<
famName << endl;
else
{
cout<< "\n\tchildren: " <<
endl;
vector<string>::iteratorit =
iter->second.begin();
while( it !=
iter->second.end() )
cout << *it++<<
endl;
}
system("pause");
return0;
}
19.把上一题的map对象再扩展一下,使其vector对象存储pair类型的对象,记录每个孩子的名字和生日。相应地修改程序,测试修改后的测试程序以检查所编写的map是否正确。
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
map<string,
vector<pair<
string,string > > >
children;
stringfamName,
childName,childBday;
do
{
cout<< "\n Input
families' name( ctrl + z to end ):"<< endl;
cin>> famName;
if( !cin )
break;
vector<pair<
string,string > >
chd;
pair<map<
string,vector<
pair<string, string> > >::iterator,
bool > ret =
children.insert(
make_pair( famName,chd ) );
if( !ret.second)
{
cout << "\nAlready exist the family name: " <<
famName<< endl;
continue;
}
cout<< "\tInput children's name and birthday( ctrl + z to end ):" <<
endl;
while( cin >>
childName>> childBday )
ret.first->second.push_back(
make_pair( childName,childBday ) );
cin.clear();
} while (
cin);
cin.clear();
cout<< "\n\tInput a family name to search:" <<
endl;
cin>> famName;
map<string,
vector<pair<
string,string > > >::iterator
iter = children.find(
famName );
if ( iter ==
children.end() )
cout<< "\n\t Sorry, there is not this familyname: " <<
famName << endl;
else
{
cout<< "\n\tThe family's children and everychild's birthday is: " <<
endl;
vector<pair<
string,string > >::iteratorit =
iter->second.begin();
while( it !=
iter->second.end() )
{
cout << (*it).first <<
" ---" << (*it).second <<
endl;
it++;
}
}
system("pause");
return0;
}
20.列出至少三种可以使用map类型的应用。为每种应用定义map对象,并指出如何插入和读取元素。
字典: map<string, string > dictionary;
电话簿:map< string, string > telBook;
超市物价表:map<string, double > priceList;
插入元素可以用:下标操作符或insert函数;用find函数读取元素。
21.解释map和set容器的差别,以及它们各自适用的情况。
差别:map容器是
K-V 对的集合, set容器是
键
的集合; map类型适用于需要了解键与值的对应情况,而set类型适用于只需判断某值是否存在的情况。
22.解释set和list容器的差别,以及它们各自适用的情况。
差别:set容器中的元素不能修改,而list容器中的元素可以修改;
set容器适用于保存元素值不变的集合,而list容器适用于保存会发生变化的元素。
23.编写程序将排除的单词存储在vector对象中,而不是存储在set对象中。请指出使用set的好处。
//11.16_10.23_restricted_wc.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<utility>
#include
<vector>
#include
<set>
#include
<map>
using
namespace std;
void
restricted_wc( vector<string>strVec,
map<string,
int>&wordCount )
{
// create aset of excluded words
set<string>
excluded;
for ( vector<string>::iterator
it = strVec.begin();
it != strVec.end(); ++it )
{
excluded.insert( *it );
}
stringword;
cout<< " Input some words to count the wordsin wordCount(map) ( ctrl + z to end): "
<<
endl;
cin.clear();
while (cin >>
word)
{
if( !excluded.count(word ) )
++wordCount[ word ];
}
}
int
_tmain(int
argc, _TCHAR* argv[])
{
cout<< " Input some words tovector<string> excluded ( ctrl + z to end): " <<
endl;
vector<string>
excludedVec;
stringexcludedWord;
while (cin >>
excludedWord)
excludedVec.push_back(
excludedWord);
// userestricted_wc()
map<string,
int > wordCount;
restricted_wc(excludedVec,
wordCount);
// show outthe map:wordCount
cout<< "\n\tShow out the map:wordCount:" <<
endl;
for ( map<
string, int >::iterator
it = wordCount.begin();
it != wordCount.end(); ++it )
{
cout<< " The word '" << (*it).first <<
"' appears for " << (*it).second <<" times. " <<
endl;
}
system("pause");
return0;
}
24.编写程序通过删除单词尾部的’s’生成单词的非负数版本。同时,建立一个单词排除集,用于识别以’s’结尾,但这个结尾的’s’又不能删除的单词。例如,放在该排除集中的单词可能有success和class。使用这个排除集编写程序,删除输入单词的复数后缀,而如果输入的是排除集中的单词,则保持该单词不变。
//11.16_10.25_exclude_s_of_words_wc.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<set>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
set<string>
excluded;
// create setof excluded words
excluded.insert(
"success");
excluded.insert(
"class");
// get rid of's'
stringword;
cout<< "\n Input some words : "<<
endl;
while (cin >>
word)
{
if( ! excluded.count(word ) )
word.resize(
word.size() - 1 );
cout<< "\n Not-plural version: "<<
word << endl
<< "\n Enter aword( ctrl+z to end) " <<
endl;
}
system("pause");
return0;
}
25.定义一个vector容器,存储你在未来六个月要阅读的书,再定义一个set,用于记录你已经看过的书名。编写程序从vector中为你选择一本没有读过而现在要读的书。当它为你返回选中的书名后,应该将该书名放入记录已读书目的set中。如果实际上你把这本书放在一边没有看,则本程序应该支持从已读书目的set中删除该书的记录。在虚拟的六个月后,输出已读书目和还没有读的书。
//11.16_10.25_book_useVector_set.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<set>
#include
<cstdlib>
#include
<ctime>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
// 1
createvector:bookVec
vector<string>strVecBook;
string
bName;
cout << " Input books' name you wanna read (ctrl+z to end):\n";
while (
cin >>bName )
strVecBook.push_back(
bName );
size_t
NumOfBooks =strVecBook.size();
// record numberof books
// 2 create set:strSetReadedBook
set<string>
strSetReadedBook;
string
strChoice, bookName;
bool
_6MonthLater = false;
// use system's time for seed of rand
srand ( (
unsigned )time(NULL) );
while ( !_6MonthLater&& !strVecBook.empty() )
{
cin.clear();
cout <<"\n ==>>Do you wanna read a book? ( Yes /No ): ";
cin >>
strChoice;
if (
strChoice[0] == 'Y'||
strChoice[0] == 'y')
{
int
i = rand() % strVecBook.size();
bookName =
strVecBook[i];
cout <<" =>> We choose this book for you: "<<
bookName << endl;
strSetReadedBook.insert(
bookName );
strVecBook.erase(
strVecBook.begin() +
i );
cout <<"\tOne month later ... ... "<<
endl
<< " Didyou read this book? ( Yes/ No ): ";
//cin.clear();
cin >>
strChoice;
if (
strChoice[0] == 'n'||
strChoice[0] == 'N')
{
// delete the bookfrom strSetReadedBook
and add tostrVecBook
strSetReadedBook.erase(
bookName );
strVecBook.push_back(
bookName);
}
}
// 6 month later ?
cout <<"
6month later ? ( Yes / No ): ";
//cin.clear();
cin >>
strChoice;
if (
strChoice[0] == 'Y'||
strChoice[0] == 'y')
_6MonthLater = true;
}
if (
_6MonthLater )
{
if ( !strSetReadedBook.empty())
{
cout <<"\n ==>>>During the latest 6 months ,you read: \n\t";
for (
set<string>::iterator
iter = strSetReadedBook.begin();iter !=
strSetReadedBook.end(); ++iter )
{
cout <<*iter <<
" ";
}
}
if ( !strVecBook.empty())
{
cout <<"\n ==>>>The books you have not read :\n\t";
for (
vector<string>::iterator
it = strVecBook.begin();it !=
strVecBook.end(); ++it )
{
cout <<*it <<
" ";
}
}
cout <<endl;
}
if (
strSetReadedBook.size() ==
NumOfBooks)
cout << "\tGood, you read all the books. " <<
endl;
system("pause");
return0;
}
26.编写程序建立作者及其作品的multimap容器,使用find函数在multimap中查找元素,并调用erase将其删除。当所寻找的元素不存在时,确保你的程序依然能正确执行。
//11.16_10.26_multimap.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<map>
#include
<utility>
#include
<string>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
multimap<string,
string> mmapAuthor;
stringauthor,
book;
cout<< " \t=>> Input Author and hisBook( ctrl + z to end ):\n";
while (cin >>
author>> book )
{
mmapAuthor.insert(
make_pair( author,
book ) );
}
// search
stringstrSearchItem;
cout<< "\n\t=>> Input which author doyou wanna search: ";
cin.clear();
cin>> strSearchItem;
multimap<string,
string>::iterator
iter= mmapAuthor.find(strSearchItem );
typedefmultimap<string,string >::size_typesz_type;
sz_typeamount =
mmapAuthor.count(
strSearchItem);
for ( sz_type
cnt = 0; cnt !=
amount; ++cnt, ++iter )
{
cout<< "\n\t ^_^ We got the author:
" << iter->first<<
endl
<< "\t =>> his book:\t" <<
iter->second<<
endl;
}
// erase
amount= mmapAuthor.erase(strSearchItem );
if ( amount )
{
cout<< "\n\tWe successfully to erase "<<
amount << " books of the author. " <<
endl;
}
cout<< endl;
system("pause");
return0;
}
27.重复上一题所编写的程序,但这一次要求使用equal_range函数获取迭代器,然后删除一段范围内的元素。
//11.16_10.27_equal_range.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<map>
#include
<utility>
#include
<string>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
multimap<string,
string> mmapAuthor;
stringauthor,
book;
cout<< " \t=>> Input Author and hisBook( ctrl + z to end ):\n";
while (cin >>
author>> book )
{
mmapAuthor.insert(
make_pair( author,
book ) );
}
// search
stringstrSearchItem;
cout<< "\n\t=>> Input which author doyou wanna search: ";
cin.clear();
cin>> strSearchItem;
typedefmultimap<string,string >::size_typesz_type;
typedefmultimap<string,string >::iteratorauthor_it;
sz_typeamount =
mmapAuthor.count(
strSearchItem);
pair<author_it,
author_it> pos =
mmapAuthor.equal_range(
strSearchItem);
author_itit =
pos.first;
// record theiterator
while (it!=
pos.second )
{
cout<< "\n\t ^_^ We got the author:
" << it->first<<
endl
<< "\t =>> his book:\t" <<
it->second<< endl;
++it;
}
// erase
mmapAuthor.erase(
pos.first,
pos.second );
if ( amount )
{
cout<< "\n\tWe successfully to erase "<<
amount << " books of the author. " <<
endl;
}
cout<< "\n\t=>> Now the content ofmultimap is:" <<
endl;
for ( author_it
iter = mmapAuthor.begin();iter !=
mmapAuthor.end(); ++iter )
{
cout<<
iter->first<<
" ---- " << iter->second<<
endl;
}
cout<< endl;
system("pause");
return0;
}
28.沿用上题中的multimap容器,编写程序以下面的格式按姓名首字母的顺序输出作者名字:
AuthorNames Bginning with ‘A’:
Author,book, book, …
…
AuthorNames Beginning with ‘B’:
…
//11.16_10.28_multimap_Author_and_work.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<map>
#include
<utility>
#include
<string>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
multimap<string,
string> mmapAuthor;
stringauthor,
book;
cin.clear();
cout<< " \t=>> Input Author and hisBook( ctrl + z to end ):\n";
while (cin >>
author>> book )
{
mmapAuthor.insert(
make_pair( author,
book ) );
}
typedefmultimap<string,string >::iteratorauthor_it;
author_ititer =
mmapAuthor.begin();
if ( iter ==
mmapAuthor.end() )
{
cout<< "\n\t Empty multimap! "<<
endl;
return0;
}
stringcurrAuthor,
preAuthor;
do
{
currAuthor= iter->first;
if( preAuthor.empty()||
currAuthor[0] != preAuthor[0])
{
cout << "AuthorNames Beginning with ' "
<< iter->first[0]<<
" ' : " << endl;
}
cout<< currAuthor;
pair<author_it,
author_it> pos =
mmapAuthor.equal_range(
iter->first );
while( pos.first!=
pos.second)
{
cout << "," <<
pos.first->second;
++pos.first;
}
cout<< endl;
iter= pos.second;
preAuthor= currAuthor;
} while( iter !=
mmapAuthor.end() );
cout<< endl;
system("pause");
return0;
}
29.解释本节最后一个程序的输出表达式使用的操作数pos.first->second
的含义。
pos存储的是一个pair对象,pos.first是指pair的第一个迭代器,pos.first->second是指那个迭代器所指的multimap对象的第二个元素,即作者的作品。
30.TextQuery类的成员函数仅使用了前面介绍过的内容。先别查看后面章节,请自己编写这些成员函数。提示:唯一棘手的是run_query函数在行号集合set为空时应返回什么值?解决方法是构造并返回一个新的(临时)set对象。
#include
"stdafx.h"
#include
"TextQuery.h"
#include
<sstream>
string
TextQuery::text_line(
line_no line )
const
{
if ( line <
lines_of_text.size() )
{
returnlines_of_text[
line];
}
throw out_of_range(
"Linenumber out of range " );
}
void
TextQuery::store_file(
ifstream&is )
{
stringtextline;
while (getline(
is, textline ) )
{
lines_of_text.push_back(
textline);
}
}
void
TextQuery::build_map()
{
for ( line_no
line_num =0; line_num !=
lines_of_text.size(); ++
line_num)
{
istringstreamline(
lines_of_text[line_num ] );
stringword;
while( line >>
word)
{
// get rid of punctuation
word = cleanup_str(word );
if ( word_map.count (
word ) == 0)
word_map[word].push_back(
line_num);
else
{
if ( line_num !=
word_map[word].back() )
word_map[word].push_back(
line_num);
}
}
}
}
vector<
TextQuery::line_no >
TextQuery::run_query(
const string&
query_word ) const
{
map<string,
vector<line_no> >::const_iteratorloc =
word_map.find(
query_word );
if ( loc ==
word_map.end() )
{
returnvector<line_no>();
}
else
returnloc->second;
}
// get ridof punctuation
string
TextQuery::cleanup_str(
const string &word )
{
stringret;
for ( string ::const_iteratorit =
word.begin();
it != word.end(); ++it )
{
if( !ispunct( *it) )
ret += tolower( *it );
}
return ret;
}
31.如果没有找到要查询的单词,main函数输出什么?
strWord occurs 0 times.
32.重新实现文本查询程序,使用vector容器代替set对象来存储行号。注意,由于行以升序出现,因此只有在当行号不是vector容器对象中的最后一个元素时,才能将新的行号添加到vector中。这两种实现方法的性能特点和设计特点分别是什么?你觉得哪一种解决方法更好?为什么?
使用vector容器存储行号,在进行行号的插入时,为了不存储重复的行号,需先判断该行号是否已存在容器中,再决定是否插入:而使用set容器存储行号,则可以利用set容器所提供的insert函数的特点,无需判断行号是否已存在,直接利用insert函数插入元素即可,性能较高。
vector容器的特点是适合用于需随机访问元素的情况。而此处对于存储的行号,不需进行随机访问,只需进行顺序访问,因此使用set容器操作简单,设计更为简洁,因此使用set容器好。
//11.17_10.32_TextQuery.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
"TextQuery.h"
#include
"functions.h"
string
make_plural ( size_t ,
const string &,
const string & );
ifstream&
open_file( ifstream&,
conststring& );
void
print_results( const
vector<TextQuery::line_no>&
locs, conststring &
sought,const
TextQuery&file )
{
typedefvector<
TextQuery::line_no >
line_nums;
line_nums::size_type
size = locs.size();
cout<< "\n" <<
sought << "occurs "
<< size << "
" << make_plural(
size, "time",
"s") << endl;
line_nums::const_iterator
it =locs.begin();
for ( ;it !=
locs.end(); ++it )
{
cout<< "\t( line " << (*it) + 1 <<
") " <<
file.text_line( *it ) <<
endl;
}
}
int
_tmain(int
argc, _TCHAR* argv[])
{
ifstreaminfile;
if ( argc < 2 || !open_file(infile,
argv[1]) )
{
cerr<< " No input file! "<<
endl;
returnEXIT_FAILURE;
}
TextQuerytq;
tq.store_file(
infile);
while (true )
{
cout<< "Input word to look for, or q to quit:";
strings;
cin>> s;
stringret;
for( string::const_iteratorit =
s.begin();
it != s.end(); ++it )
{
ret += tolower( *it );
}
s= ret;
if( !cin ||
s== "q" ||
s== "Q" ) break;
vector<TextQuery::line_no>
locs = tq.run_query(
s );
print_results(locs,
s, tq );
}
system("pause" );
return0;
}
#ifndef
__TEXTQUERY_
#define
_TEXTQUERY_
#include
<map>
#include
<utility>
#include
<string>
#include
<vector>
#include
<iostream>
#include
<fstream>
#include
<cstring>
using
namespace std;
class
TextQuery
{
public:
typedefstring::size_typestr_size;
typedefvector<string>::size_type
line_no;
// interface
void read_file (
ifstream&is )
{
store_file(is );
build_map();
}
vector<line_no>
run_query( const
string&) const;
stringtext_line (
line_no) const;
void store_file(
ifstream&);
private:
void build_map();
vector<string>
lines_of_text;
map<string,
vector<line_no> >
word_map;
static string
cleanup_str(const
string&);
};
#endif
#include
"stdafx.h"
#include
"TextQuery.h"
#include
<sstream>
string
TextQuery::text_line(
line_no line )
const
{
if ( line <
lines_of_text.size() )
{
returnlines_of_text[
line];
}
throw out_of_range(
"Linenumber out of range " );
}
void
TextQuery::store_file(
ifstream&is )
{
stringtextline;
while (getline(
is, textline ) )
{
lines_of_text.push_back(
textline);
}
}
void
TextQuery::build_map()
{
for ( line_no
line_num =0; line_num !=
lines_of_text.size(); ++
line_num)
{
istringstreamline(
lines_of_text[line_num ] );
stringword;
while( line >>
word)
{
// get rid of punctuation
word = cleanup_str(word );
if ( word_map.count (
word ) == 0)
word_map[word].push_back(
line_num);
else
{
if ( line_num !=
word_map[word].back() )
word_map[word].push_back(
line_num);
}
}
}
}
vector<
TextQuery::line_no >
TextQuery::run_query(
const string&
query_word ) const
{
map<string,
vector<line_no> >::const_iteratorloc =
word_map.find(
query_word );
if ( loc ==
word_map.end() )
{
returnvector<line_no>();
}
else
returnloc->second;
}
// get ridof punctuation
string
TextQuery::cleanup_str(
const string &word )
{
stringret;
for ( string ::const_iteratorit =
word.begin();
it != word.end(); ++it )
{
if( !ispunct( *it) )
ret += tolower( *it );
}
return ret;
}
#include
<fstream>
#include
<string>
using
namespace std;
string
make_plural( size_t
ctr, const
string &word , const
string &ending)
{
return( ctr == 1 ) ?
word: word +
ending;
}
ifstream&
open_file( ifstream &in,
const string &file )
{
in.close();
in.clear();
in.open(
file.c_str() );
return in;
}
33.TextQuery::text_line函数为什么不检查它的参数是否为负数?
因为print_results
函数调用text_line函数时,传递由run_query函数所获取的set对象中的元素为实参,而由build_map函数生成的map容器里,set对象中出现的元素不可能为负数。
我的C++Primer作业,
2010年11月6日
Davy
//11.16_10.1_pair.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<utility>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
stringstr;
int iVal;
cout<< "\tInput some pair contents(pair<string, int > ) (^Z to end):\n";
vector<pair<string,int> >
pairVec;
while( cin >>
str>> iVal )
pairVec.push_back(
pair<string,
int> ( str, iVal));
cout<< "\n\t The content of pairVecis:\n" ;
for ( vector<
pair<string,
int>>::iterator
it= pairVec.begin();it !=
pairVec.end(); ++it )
{
cout<< it->first
<< " " <<
it->second<< endl;
}
system("pause");
return0;
}
2.在前一题中,至少可使用三种方法创建pair对象。编写三个版本的程序,分别采用不同的方法来创建pair对象。你认为哪一种方法更易于编写和理解,为什么?
stringstr;
int iVal;
vector<pair<string,int> >
pairVec;
pair<string,
int> newPair;
while( cin >> str >> iVal )
{
// first method
pairVec.push_back(pair< string, int > ( str, iVal) );
//the second method
newPair = make_pair( str,iVal );
pairVec.push_back( newPair );
}
// thirdmethod
while (cin >>
newPair.first >>
newPair.second )
{
pairVec.push_back(
newPair);
}
第二种方法更好一些,更容易阅读和理解。因为它调用了make_pair函数,可以明确地表明确实生成了pair对象这一行为。
3.描述关联容器和顺序容器的差别。
两者的本质差别在于:关联容器通过键 key
存储和读取元素,
而顺序容器则通过元素在容器中的位置顺序存储和访问元素。
4.举例说明list、vector、deque、map以及set类型分别使用的情况。
list类型适用于需要在容器的中间位置插入和删除元素的情况,如以无序的方式读入一系列学生的数据;
vector类型适用于需要随机访问元素的情况。如:在学号为1…n的学生中,访问第
x 学号的学生的信息。
deque类型适用于在容器的尾部或首部有插入和删除元素情况。如:对服务窗口先来先服务的情况。
map适用于需要
key – value 对的集合的情况。如:字典电和话簿的建立和使用。
set类型适用于使用键集合的情况。例如,黑名单的建立和使用。
5.定义一个Map对象,将单词与一个list对象关联起来,该list对象存储对应的单词可能出现的行号。
map< string, list<int> > wordLines;
6.可否定义一个map对象以vector<int>::interator为键关联int型对象?如果以list<int>::iterator关联int型对象呢?或者,以pair<int,
string>关联int?对于每种情况,如果允许,请解释其原因。
可以定义一个map对象以vector<int>::iterator
和 pair< int,string >
为键关联int
型对象。
不能定义第二种情况,因为键类型必须支持 <
操作,而list容器的迭代器类型不支持<操作。
pair<int, string>关联int可以。
7.对于以int型对象为索引关联vector<int>型对象的map容器,它的mapped_type。key_type和value_type分别是什么?
分别是: vector<int> , int
和 pair< const int, vector<int> >
。
8.编写一个表达式,使用map的迭代器给其元素赋值。
map< string, int > m;
map< string, int >::iterator map_it = m.begin();
map_it->second = val;
// 只能对map的值成员元素赋值。不能对键进行赋值。
9.编写程序统计并输出所读入的单词出现的次数。
//11.16_10.9_wordcount.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
map<string,
int > wordCount;
stringword;
while (cin >>
word)
{
++wordCount[word ];
cout<< " The word '" <<
word << "'appears for "
<< wordCount[ word ]<<
" times." << endl;
}
system("pause");
return0;
}
10.解释下面程序的功能:
map<int,int> m;
m[0]= 1;
比较上一程序和下面程序的行为
vector<int>v;
v[0]= 1;
首先创建一个空的map容器m,然后再m中增加一个键为0的元素,并将其值赋值为1,
第二段程序将出现运行时错误,因为v为空的vector对象,其中下标为0的元素不存在。对于vector容器,不能对尚不存在的元素直接赋值,只能使用push_back、insert等函数增加元素。
11.哪些类型可用做map容器对象的下标?下标操作符返回的又是什么类型?给出一个具体例子说明,即定义一个map对象,指出哪些类型可用作其下标,以及下标操作符返回的类型。
可用作map容器的对象的下标必须是支持
< 操作符的类型。
下标操作符的返回类型为map容器中定义的mapped_type类型。
如 map< string, int >wordCount;
可用于其下标的类型为string类型以及C风格字符串类型,下标的操作符返回int类型。
12.重写10.3.4节习题的单词统计程序,要求使用insert函数代替下标运算。你认为哪个程序更容易编写和阅读?请解释原因。
//11.16_10.12_wordCount_insert.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
cout<< " Input some words ( ctrl + z to end):" <<
endl;
map<string,
int > wordCount;
stringword;
while (cin >>
word)
{
pair<map<
string,int >::iterator,
bool > ret=
wordCount.insert(
make_pair( word, 1 ) );
if( !ret.second)
++ret.first->second;
}
for ( map<
string, int >::iterator
it = wordCount.begin();
it != wordCount.end(); ++it )
{
cout<< " The word '" << (*it).first <<
"' appears for " << (*it).second <<" times. " <<
endl;
}
system("pause");
return0;
}
13.假设有map<string,vector<int> >类型,指出在该容器中插入一个元素的insert函数应具有的参数类型和返回值类型。
参数类型:pair<const string, vector<int> >
返回值类型:pair<map< string, vector<int> >::iterator , bool >
14.map容器的count和find运算有何区别?
前者返回map容器中给定的键K的出现次数,且返回值只能是0或者1,因为map只允许一个键对应一个实例。
后者返回给定键K元素索引时
指向元素的迭代器,若不存在此K值,则返回超出末端迭代器。
15.你认为count适合用于解决哪一类问题?而find呢?
count适合用于判断map容器中某键是否存在,find适合用于在map容器中查找指定键对应元素。
16.定义并初始化一个变量,原来存储调用键为string,值为vector<int>的map对象的find函数的返回结果。
map< string, vector<int> >::iterator it = xMap.find( K);
17.上述转换程序使用了find函数来查找单词:
map<string,string>::const_iterator map_it = trans_map.find(word);
你认为这个程序为什么要使用find函数?如果使用下标操作符又会怎样?
使用find函数,是为了当文本中出现的单词word是要转换的单词时,获取该元素的迭代器,以便获取对应的转换后的单词。
如果使用下标,则必须先通过使用count函数来判断元素是否存在,否则,当元素不存在时,会创建新的元素并插入到容器中从而导致单词转换结果不是预期效果
18.定义一个map对象,其元素的键是家族姓氏,而值则是存储该家族孩子名字的vector对象。为这个map容器输入至少六个条目。通过基于家族姓氏的查询检测你的程序,查询应输出该家族所有孩子的名字。
//11..16_10.18_map_vector_children'sName.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
map<string,
vector<string> >
children;
stringfamName,
childName;
do
{
cout<< " Input
families' name( ctrl + z to end ): "<< endl;
cin>> famName;
if( !cin )
break;
vector<string>
chd;
pair<map<
string,vector<string>>::iterator,
bool> ret =
children.insert(
make_pair( famName,chd ) );
if( !ret.second)
{
cout << "Already exist the family name: " <<
famName<< endl;
continue;
}
cout<< "\n\tInput children's name ( ctrl + zto end ): " <<
endl;
while( cin >>
childName)
ret.first->second.push_back(
childName );
cin.clear();
} while (
cin);
cin.clear();
cout<< "\n\tInput a family name to search:" <<
endl;
cin>> famName;
map<string,
vector<string> >::iteratoriter =
children.find(
famName );
if ( iter ==
children.end() )
cout<< "\n\t Sorry, there is not this familyname: " <<
famName << endl;
else
{
cout<< "\n\tchildren: " <<
endl;
vector<string>::iteratorit =
iter->second.begin();
while( it !=
iter->second.end() )
cout << *it++<<
endl;
}
system("pause");
return0;
}
19.把上一题的map对象再扩展一下,使其vector对象存储pair类型的对象,记录每个孩子的名字和生日。相应地修改程序,测试修改后的测试程序以检查所编写的map是否正确。
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<utility>
#include
<map>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
map<string,
vector<pair<
string,string > > >
children;
stringfamName,
childName,childBday;
do
{
cout<< "\n Input
families' name( ctrl + z to end ):"<< endl;
cin>> famName;
if( !cin )
break;
vector<pair<
string,string > >
chd;
pair<map<
string,vector<
pair<string, string> > >::iterator,
bool > ret =
children.insert(
make_pair( famName,chd ) );
if( !ret.second)
{
cout << "\nAlready exist the family name: " <<
famName<< endl;
continue;
}
cout<< "\tInput children's name and birthday( ctrl + z to end ):" <<
endl;
while( cin >>
childName>> childBday )
ret.first->second.push_back(
make_pair( childName,childBday ) );
cin.clear();
} while (
cin);
cin.clear();
cout<< "\n\tInput a family name to search:" <<
endl;
cin>> famName;
map<string,
vector<pair<
string,string > > >::iterator
iter = children.find(
famName );
if ( iter ==
children.end() )
cout<< "\n\t Sorry, there is not this familyname: " <<
famName << endl;
else
{
cout<< "\n\tThe family's children and everychild's birthday is: " <<
endl;
vector<pair<
string,string > >::iteratorit =
iter->second.begin();
while( it !=
iter->second.end() )
{
cout << (*it).first <<
" ---" << (*it).second <<
endl;
it++;
}
}
system("pause");
return0;
}
20.列出至少三种可以使用map类型的应用。为每种应用定义map对象,并指出如何插入和读取元素。
字典: map<string, string > dictionary;
电话簿:map< string, string > telBook;
超市物价表:map<string, double > priceList;
插入元素可以用:下标操作符或insert函数;用find函数读取元素。
21.解释map和set容器的差别,以及它们各自适用的情况。
差别:map容器是
K-V 对的集合, set容器是
键
的集合; map类型适用于需要了解键与值的对应情况,而set类型适用于只需判断某值是否存在的情况。
22.解释set和list容器的差别,以及它们各自适用的情况。
差别:set容器中的元素不能修改,而list容器中的元素可以修改;
set容器适用于保存元素值不变的集合,而list容器适用于保存会发生变化的元素。
23.编写程序将排除的单词存储在vector对象中,而不是存储在set对象中。请指出使用set的好处。
//11.16_10.23_restricted_wc.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<utility>
#include
<vector>
#include
<set>
#include
<map>
using
namespace std;
void
restricted_wc( vector<string>strVec,
map<string,
int>&wordCount )
{
// create aset of excluded words
set<string>
excluded;
for ( vector<string>::iterator
it = strVec.begin();
it != strVec.end(); ++it )
{
excluded.insert( *it );
}
stringword;
cout<< " Input some words to count the wordsin wordCount(map) ( ctrl + z to end): "
<<
endl;
cin.clear();
while (cin >>
word)
{
if( !excluded.count(word ) )
++wordCount[ word ];
}
}
int
_tmain(int
argc, _TCHAR* argv[])
{
cout<< " Input some words tovector<string> excluded ( ctrl + z to end): " <<
endl;
vector<string>
excludedVec;
stringexcludedWord;
while (cin >>
excludedWord)
excludedVec.push_back(
excludedWord);
// userestricted_wc()
map<string,
int > wordCount;
restricted_wc(excludedVec,
wordCount);
// show outthe map:wordCount
cout<< "\n\tShow out the map:wordCount:" <<
endl;
for ( map<
string, int >::iterator
it = wordCount.begin();
it != wordCount.end(); ++it )
{
cout<< " The word '" << (*it).first <<
"' appears for " << (*it).second <<" times. " <<
endl;
}
system("pause");
return0;
}
24.编写程序通过删除单词尾部的’s’生成单词的非负数版本。同时,建立一个单词排除集,用于识别以’s’结尾,但这个结尾的’s’又不能删除的单词。例如,放在该排除集中的单词可能有success和class。使用这个排除集编写程序,删除输入单词的复数后缀,而如果输入的是排除集中的单词,则保持该单词不变。
//11.16_10.25_exclude_s_of_words_wc.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<set>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
set<string>
excluded;
// create setof excluded words
excluded.insert(
"success");
excluded.insert(
"class");
// get rid of's'
stringword;
cout<< "\n Input some words : "<<
endl;
while (cin >>
word)
{
if( ! excluded.count(word ) )
word.resize(
word.size() - 1 );
cout<< "\n Not-plural version: "<<
word << endl
<< "\n Enter aword( ctrl+z to end) " <<
endl;
}
system("pause");
return0;
}
25.定义一个vector容器,存储你在未来六个月要阅读的书,再定义一个set,用于记录你已经看过的书名。编写程序从vector中为你选择一本没有读过而现在要读的书。当它为你返回选中的书名后,应该将该书名放入记录已读书目的set中。如果实际上你把这本书放在一边没有看,则本程序应该支持从已读书目的set中删除该书的记录。在虚拟的六个月后,输出已读书目和还没有读的书。
//11.16_10.25_book_useVector_set.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<string>
#include
<vector>
#include
<set>
#include
<cstdlib>
#include
<ctime>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
// 1
createvector:bookVec
vector<string>strVecBook;
string
bName;
cout << " Input books' name you wanna read (ctrl+z to end):\n";
while (
cin >>bName )
strVecBook.push_back(
bName );
size_t
NumOfBooks =strVecBook.size();
// record numberof books
// 2 create set:strSetReadedBook
set<string>
strSetReadedBook;
string
strChoice, bookName;
bool
_6MonthLater = false;
// use system's time for seed of rand
srand ( (
unsigned )time(NULL) );
while ( !_6MonthLater&& !strVecBook.empty() )
{
cin.clear();
cout <<"\n ==>>Do you wanna read a book? ( Yes /No ): ";
cin >>
strChoice;
if (
strChoice[0] == 'Y'||
strChoice[0] == 'y')
{
int
i = rand() % strVecBook.size();
bookName =
strVecBook[i];
cout <<" =>> We choose this book for you: "<<
bookName << endl;
strSetReadedBook.insert(
bookName );
strVecBook.erase(
strVecBook.begin() +
i );
cout <<"\tOne month later ... ... "<<
endl
<< " Didyou read this book? ( Yes/ No ): ";
//cin.clear();
cin >>
strChoice;
if (
strChoice[0] == 'n'||
strChoice[0] == 'N')
{
// delete the bookfrom strSetReadedBook
and add tostrVecBook
strSetReadedBook.erase(
bookName );
strVecBook.push_back(
bookName);
}
}
// 6 month later ?
cout <<"
6month later ? ( Yes / No ): ";
//cin.clear();
cin >>
strChoice;
if (
strChoice[0] == 'Y'||
strChoice[0] == 'y')
_6MonthLater = true;
}
if (
_6MonthLater )
{
if ( !strSetReadedBook.empty())
{
cout <<"\n ==>>>During the latest 6 months ,you read: \n\t";
for (
set<string>::iterator
iter = strSetReadedBook.begin();iter !=
strSetReadedBook.end(); ++iter )
{
cout <<*iter <<
" ";
}
}
if ( !strVecBook.empty())
{
cout <<"\n ==>>>The books you have not read :\n\t";
for (
vector<string>::iterator
it = strVecBook.begin();it !=
strVecBook.end(); ++it )
{
cout <<*it <<
" ";
}
}
cout <<endl;
}
if (
strSetReadedBook.size() ==
NumOfBooks)
cout << "\tGood, you read all the books. " <<
endl;
system("pause");
return0;
}
26.编写程序建立作者及其作品的multimap容器,使用find函数在multimap中查找元素,并调用erase将其删除。当所寻找的元素不存在时,确保你的程序依然能正确执行。
//11.16_10.26_multimap.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<map>
#include
<utility>
#include
<string>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
multimap<string,
string> mmapAuthor;
stringauthor,
book;
cout<< " \t=>> Input Author and hisBook( ctrl + z to end ):\n";
while (cin >>
author>> book )
{
mmapAuthor.insert(
make_pair( author,
book ) );
}
// search
stringstrSearchItem;
cout<< "\n\t=>> Input which author doyou wanna search: ";
cin.clear();
cin>> strSearchItem;
multimap<string,
string>::iterator
iter= mmapAuthor.find(strSearchItem );
typedefmultimap<string,string >::size_typesz_type;
sz_typeamount =
mmapAuthor.count(
strSearchItem);
for ( sz_type
cnt = 0; cnt !=
amount; ++cnt, ++iter )
{
cout<< "\n\t ^_^ We got the author:
" << iter->first<<
endl
<< "\t =>> his book:\t" <<
iter->second<<
endl;
}
// erase
amount= mmapAuthor.erase(strSearchItem );
if ( amount )
{
cout<< "\n\tWe successfully to erase "<<
amount << " books of the author. " <<
endl;
}
cout<< endl;
system("pause");
return0;
}
27.重复上一题所编写的程序,但这一次要求使用equal_range函数获取迭代器,然后删除一段范围内的元素。
//11.16_10.27_equal_range.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<map>
#include
<utility>
#include
<string>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
multimap<string,
string> mmapAuthor;
stringauthor,
book;
cout<< " \t=>> Input Author and hisBook( ctrl + z to end ):\n";
while (cin >>
author>> book )
{
mmapAuthor.insert(
make_pair( author,
book ) );
}
// search
stringstrSearchItem;
cout<< "\n\t=>> Input which author doyou wanna search: ";
cin.clear();
cin>> strSearchItem;
typedefmultimap<string,string >::size_typesz_type;
typedefmultimap<string,string >::iteratorauthor_it;
sz_typeamount =
mmapAuthor.count(
strSearchItem);
pair<author_it,
author_it> pos =
mmapAuthor.equal_range(
strSearchItem);
author_itit =
pos.first;
// record theiterator
while (it!=
pos.second )
{
cout<< "\n\t ^_^ We got the author:
" << it->first<<
endl
<< "\t =>> his book:\t" <<
it->second<< endl;
++it;
}
// erase
mmapAuthor.erase(
pos.first,
pos.second );
if ( amount )
{
cout<< "\n\tWe successfully to erase "<<
amount << " books of the author. " <<
endl;
}
cout<< "\n\t=>> Now the content ofmultimap is:" <<
endl;
for ( author_it
iter = mmapAuthor.begin();iter !=
mmapAuthor.end(); ++iter )
{
cout<<
iter->first<<
" ---- " << iter->second<<
endl;
}
cout<< endl;
system("pause");
return0;
}
28.沿用上题中的multimap容器,编写程序以下面的格式按姓名首字母的顺序输出作者名字:
AuthorNames Bginning with ‘A’:
Author,book, book, …
…
AuthorNames Beginning with ‘B’:
…
//11.16_10.28_multimap_Author_and_work.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
<map>
#include
<utility>
#include
<string>
using
namespace std;
int
_tmain(int
argc, _TCHAR* argv[])
{
multimap<string,
string> mmapAuthor;
stringauthor,
book;
cin.clear();
cout<< " \t=>> Input Author and hisBook( ctrl + z to end ):\n";
while (cin >>
author>> book )
{
mmapAuthor.insert(
make_pair( author,
book ) );
}
typedefmultimap<string,string >::iteratorauthor_it;
author_ititer =
mmapAuthor.begin();
if ( iter ==
mmapAuthor.end() )
{
cout<< "\n\t Empty multimap! "<<
endl;
return0;
}
stringcurrAuthor,
preAuthor;
do
{
currAuthor= iter->first;
if( preAuthor.empty()||
currAuthor[0] != preAuthor[0])
{
cout << "AuthorNames Beginning with ' "
<< iter->first[0]<<
" ' : " << endl;
}
cout<< currAuthor;
pair<author_it,
author_it> pos =
mmapAuthor.equal_range(
iter->first );
while( pos.first!=
pos.second)
{
cout << "," <<
pos.first->second;
++pos.first;
}
cout<< endl;
iter= pos.second;
preAuthor= currAuthor;
} while( iter !=
mmapAuthor.end() );
cout<< endl;
system("pause");
return0;
}
29.解释本节最后一个程序的输出表达式使用的操作数pos.first->second
的含义。
pos存储的是一个pair对象,pos.first是指pair的第一个迭代器,pos.first->second是指那个迭代器所指的multimap对象的第二个元素,即作者的作品。
30.TextQuery类的成员函数仅使用了前面介绍过的内容。先别查看后面章节,请自己编写这些成员函数。提示:唯一棘手的是run_query函数在行号集合set为空时应返回什么值?解决方法是构造并返回一个新的(临时)set对象。
#include
"stdafx.h"
#include
"TextQuery.h"
#include
<sstream>
string
TextQuery::text_line(
line_no line )
const
{
if ( line <
lines_of_text.size() )
{
returnlines_of_text[
line];
}
throw out_of_range(
"Linenumber out of range " );
}
void
TextQuery::store_file(
ifstream&is )
{
stringtextline;
while (getline(
is, textline ) )
{
lines_of_text.push_back(
textline);
}
}
void
TextQuery::build_map()
{
for ( line_no
line_num =0; line_num !=
lines_of_text.size(); ++
line_num)
{
istringstreamline(
lines_of_text[line_num ] );
stringword;
while( line >>
word)
{
// get rid of punctuation
word = cleanup_str(word );
if ( word_map.count (
word ) == 0)
word_map[word].push_back(
line_num);
else
{
if ( line_num !=
word_map[word].back() )
word_map[word].push_back(
line_num);
}
}
}
}
vector<
TextQuery::line_no >
TextQuery::run_query(
const string&
query_word ) const
{
map<string,
vector<line_no> >::const_iteratorloc =
word_map.find(
query_word );
if ( loc ==
word_map.end() )
{
returnvector<line_no>();
}
else
returnloc->second;
}
// get ridof punctuation
string
TextQuery::cleanup_str(
const string &word )
{
stringret;
for ( string ::const_iteratorit =
word.begin();
it != word.end(); ++it )
{
if( !ispunct( *it) )
ret += tolower( *it );
}
return ret;
}
31.如果没有找到要查询的单词,main函数输出什么?
strWord occurs 0 times.
32.重新实现文本查询程序,使用vector容器代替set对象来存储行号。注意,由于行以升序出现,因此只有在当行号不是vector容器对象中的最后一个元素时,才能将新的行号添加到vector中。这两种实现方法的性能特点和设计特点分别是什么?你觉得哪一种解决方法更好?为什么?
使用vector容器存储行号,在进行行号的插入时,为了不存储重复的行号,需先判断该行号是否已存在容器中,再决定是否插入:而使用set容器存储行号,则可以利用set容器所提供的insert函数的特点,无需判断行号是否已存在,直接利用insert函数插入元素即可,性能较高。
vector容器的特点是适合用于需随机访问元素的情况。而此处对于存储的行号,不需进行随机访问,只需进行顺序访问,因此使用set容器操作简单,设计更为简洁,因此使用set容器好。
//11.17_10.32_TextQuery.cpp :
定义控制台应用程序的入口点。
//
#include
"stdafx.h"
#include
<iostream>
#include
"TextQuery.h"
#include
"functions.h"
string
make_plural ( size_t ,
const string &,
const string & );
ifstream&
open_file( ifstream&,
conststring& );
void
print_results( const
vector<TextQuery::line_no>&
locs, conststring &
sought,const
TextQuery&file )
{
typedefvector<
TextQuery::line_no >
line_nums;
line_nums::size_type
size = locs.size();
cout<< "\n" <<
sought << "occurs "
<< size << "
" << make_plural(
size, "time",
"s") << endl;
line_nums::const_iterator
it =locs.begin();
for ( ;it !=
locs.end(); ++it )
{
cout<< "\t( line " << (*it) + 1 <<
") " <<
file.text_line( *it ) <<
endl;
}
}
int
_tmain(int
argc, _TCHAR* argv[])
{
ifstreaminfile;
if ( argc < 2 || !open_file(infile,
argv[1]) )
{
cerr<< " No input file! "<<
endl;
returnEXIT_FAILURE;
}
TextQuerytq;
tq.store_file(
infile);
while (true )
{
cout<< "Input word to look for, or q to quit:";
strings;
cin>> s;
stringret;
for( string::const_iteratorit =
s.begin();
it != s.end(); ++it )
{
ret += tolower( *it );
}
s= ret;
if( !cin ||
s== "q" ||
s== "Q" ) break;
vector<TextQuery::line_no>
locs = tq.run_query(
s );
print_results(locs,
s, tq );
}
system("pause" );
return0;
}
#ifndef
__TEXTQUERY_
#define
_TEXTQUERY_
#include
<map>
#include
<utility>
#include
<string>
#include
<vector>
#include
<iostream>
#include
<fstream>
#include
<cstring>
using
namespace std;
class
TextQuery
{
public:
typedefstring::size_typestr_size;
typedefvector<string>::size_type
line_no;
// interface
void read_file (
ifstream&is )
{
store_file(is );
build_map();
}
vector<line_no>
run_query( const
string&) const;
stringtext_line (
line_no) const;
void store_file(
ifstream&);
private:
void build_map();
vector<string>
lines_of_text;
map<string,
vector<line_no> >
word_map;
static string
cleanup_str(const
string&);
};
#endif
#include
"stdafx.h"
#include
"TextQuery.h"
#include
<sstream>
string
TextQuery::text_line(
line_no line )
const
{
if ( line <
lines_of_text.size() )
{
returnlines_of_text[
line];
}
throw out_of_range(
"Linenumber out of range " );
}
void
TextQuery::store_file(
ifstream&is )
{
stringtextline;
while (getline(
is, textline ) )
{
lines_of_text.push_back(
textline);
}
}
void
TextQuery::build_map()
{
for ( line_no
line_num =0; line_num !=
lines_of_text.size(); ++
line_num)
{
istringstreamline(
lines_of_text[line_num ] );
stringword;
while( line >>
word)
{
// get rid of punctuation
word = cleanup_str(word );
if ( word_map.count (
word ) == 0)
word_map[word].push_back(
line_num);
else
{
if ( line_num !=
word_map[word].back() )
word_map[word].push_back(
line_num);
}
}
}
}
vector<
TextQuery::line_no >
TextQuery::run_query(
const string&
query_word ) const
{
map<string,
vector<line_no> >::const_iteratorloc =
word_map.find(
query_word );
if ( loc ==
word_map.end() )
{
returnvector<line_no>();
}
else
returnloc->second;
}
// get ridof punctuation
string
TextQuery::cleanup_str(
const string &word )
{
stringret;
for ( string ::const_iteratorit =
word.begin();
it != word.end(); ++it )
{
if( !ispunct( *it) )
ret += tolower( *it );
}
return ret;
}
#include
<fstream>
#include
<string>
using
namespace std;
string
make_plural( size_t
ctr, const
string &word , const
string &ending)
{
return( ctr == 1 ) ?
word: word +
ending;
}
ifstream&
open_file( ifstream &in,
const string &file )
{
in.close();
in.clear();
in.open(
file.c_str() );
return in;
}
33.TextQuery::text_line函数为什么不检查它的参数是否为负数?
因为print_results
函数调用text_line函数时,传递由run_query函数所获取的set对象中的元素为实参,而由build_map函数生成的map容器里,set对象中出现的元素不可能为负数。
我的C++Primer作业,
2010年11月6日
Davy
相关文章推荐
- 【C++ Primer】【学习笔记】【第十章】关联容器之:pair类型
- C++ Primer 4 第十章 关联容器
- C++ Primer 第十章 关联容器
- C++ Primer 第十章 关联容器
- 【C++ Primer】【学习笔记】【第十章】关联容器之:multimap和multiset类型
- 【C++ Primer】【学习笔记】【第十章】关联容器之:set类型
- 【C++ Primer】【学习笔记】【第十章】关联容器之:文本查询程序
- 第十章-关联容器----重学C++之《 C++ PRIMER》
- 第十章-关联容器----重学C++之《 C++ PRIMER》
- 关联容器 - 1【C++ Primer 学习笔记 - 第十章】
- 【C++ Primer】【学习笔记】【第十章】关联容器之:map类型
- 《C++ Primer 4th》读书笔记 第10章-关联容器
- 关联容器 - 2【C++ Primer 学习笔记 - 第十章】
- 11、C++ Primer 4th 笔记,关联容器(1)
- C++ primer【笔记】关联容器 map
- C++ Primer 学习笔记_34_STL实践与分析(8) --引言、pair类型、关联容器
- c++ primer 10 关联容器
- C++关联容器之set类型——《C++ Primer》抄书笔记
- C++ primer 读书笔记系列——(7)关联容器
- c++ primer读书笔记-第十章 关联容器