C++ Primer(第五版)|練習題答案與解析(第十一章:關聯容器)

C++ Primer(第五版)|練習題答案與解析(第十一章:關聯容器)

本博客主要記錄C++ Primer(第五版)中的練習題答案與解析。
參考:C++ Primer
C++ Primer

練習題11.1

描述map和vector的不同。

  • vector是順序容器,只能存放單一類型的數據。
  • map是關聯的容器,存放一對key-value,值表示與索引相關聯的數據,這兩個數據可以是不同類型。(P374)

練習題11.2

分別給出最適合使用list、vector、deque、map以及set的例子。

  • list ,適用於在中間進行操作的情況。

  • vector, 構建動態數組(不確定大小)。

  • deque,適用於只需要在頭尾進行操作的情況。

  • map , 字典(Key-Value)對應。

  • set , 集合。

練習題11.3

編寫你自己的單詞計數程序。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <cctype>
using namespace std;
int main()
{
    map<string, size_t> word_count;
    string word;
    while (cin >> word) {
        ++ word_count[word];
    }

    for (const auto& w : word_count) {
        cout << w.first << " occurs " << w.second << ((w.second > 1) ?  " times" : " time.")  << endl;
    }
    return 0;
}

測試:

the C++ is very very interesting
^Z
C++ occurs 1 time.
interesting occurs 1 time.
is occurs 1 time.
the occurs 1 time.
very occurs 2 times

練習題11.4

擴展你的程序,忽略大小寫和標點。例如,“example.”、"example,"和"Example"應該遞增相同的計數器。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <cctype>
//using namespace std;
//如果直接使用std,remove_if就編譯不過,原因未知
using std::remove_if;
using std::string;
using std::cin;
using std::cout;
using std::endl;
using std::map;
int main()
{
    map<string, size_t> word_count;
    string word;
    while (cin >> word) {
        for (auto& ch : word) ch = tolower(ch);
        word.erase(remove_if(word.begin(), word.end(), ispunct), word.end());
        ++ word_count[word];
    }
    for (const auto& w : word_count) {
        cout << w.first << " occurs " << w.second << ((w.second > 1) ?  " times" : " time.")  << endl;
    }
    return 0;
}

測試:

the C++ is Very very interesting and Interesting.
^Z
and occurs 1 time.
c occurs 1 time.
interesting occurs 2 times
is occurs 1 time.
the occurs 1 time.
very occurs 2 times

ispunct瞭解
remove_if瞭解

練習題11.5

解釋map和set的區別。你如何選擇使用哪個?

P376
map必須指明關鍵字類型和值類型
set只需指明關鍵字類型,沒有值。
看情況使用。

練習題11.6

解釋set和list的區別。你如何選擇使用哪個?

set是關聯容器,進行查找、修改操作效率高。
list是順序容器,插入刪除等操作效率低。

練習題11.7

定義一個map,關鍵字是家庭的姓,值是一個vector,保存家中孩子們的名。編寫代碼,實現添加新的家庭以及向已有家庭中添加新的孩子。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
    map<string, vector<string>> Families;
    string family_name;
    vector<string> child_name;
    string name;
    int if_continue = 1;
    while (if_continue) {
        cout << "Please input the family name: ";
        cin >> family_name;
        cout << "Please input the child name: ";
        cin >> name;
        Families[family_name].push_back(name);
        cout << "Continue?(1/0)";
        cin >> if_continue;
    }

    for (const auto& f : Families) {
        cout << f.first << "\t\t";
        for (const auto n : f.second) {
            cout << n << "\t";
        }
        cout << endl;
    }
    cout << endl;
    return 0;
}

測試:

Please input the family name: Bod
Please input the child name: Jack
Continue?(1/0)1
Please input the family name: ROse
Please input the child name: Zer
Continue?(1/0)0
Bod             Jack
ROse            Zer

練習題11.8

編寫一個程序,在一個vector而不是一個set中保存不重複的單詞。使用set的優點是什麼?

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
    vector<string> exclude = { "test", "the", "C++" };
    for (string word; cout << "Enter plz:\n", cin >> word; )
    {
        auto is_excluded = binary_search(exclude.cbegin(), exclude.cend(), word);
        auto reply = is_excluded ? "excluded" : "not excluded";
        cout << reply << endl;
    }
    return 0;
}

測試:

Enter plz:
train
not excluded
Enter plz:
test
excluded
Enter plz:
go
not excluded
Enter plz:
^Z

set具有更快的查找速度。

練習題11.9

定義一個map,將單詞與一個行號的list關聯,list中保存的是單詞所出現的行號。

定義:map<string, list<size_t>> m;滿足key-value。

練習題11.10

可以定義一個vector::iterator到int的map嗎?list::iterator到int的map呢?對於這兩種情況,如果不能,解釋爲什麼。

map<vector<int>::iterator, int> m;
map<list<int>::iterator, int> l;

可以被聲明,可以編譯通過,但不能實際使用,因爲iterator不支持比較操作。(P378).要排序就得支持比較。

練習題11.11

不使用decltype重新定義bookstore。

#include <iostream>
#include <string>
#include <set>
#include "Sales_data.h"
using namespace std;
bool compareIsbn(const Sales_data& lhs, const Sales_data& rhs)
{
    return lhs.isbn() > rhs.isbn();
}
int main()
{
    using compare = bool(*)(const Sales_data&, const Sales_data&);
    multimap<Sales_data, compare> bookstore(compareIsbn);

    return 0;
}

練習題11.12

編寫程序,讀入string和int的序列,將每個string和int存入一個pair中,pair保存在一個vector中。

練習題11.13

在上一題的程序中,至少有三種創建pair的方法。編寫此程序的三個版本,分別採用不同的方法創建pair。解釋你認爲哪種形式最易於編寫和理解,爲什麼?

#include <vector>
#include <utility>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    vector<pair<string, int>> vec;
    vector<pair<string, int>> vec1;
    vector<pair<string, int>> vec2;
    vector<pair<string, int>> vec3;
    string str;
    int i;
    while (cin >> str >> i)
        vec.push_back(pair<string, int>(str, i));
        vec1.push_back(make_pair(str, i));
        vec2.push_back({ str, i });
        vec3.emplace_back(str, i); //最簡單
    cout<<"vec :";
    for (const auto &p : vec)
        cout << p.first << ":" << p.second << endl;
    cout<<"vec1:";
    for (const auto &p : vec1)
        cout << p.first << ":" << p.second << endl;
    cout<<"vec2:";
    for (const auto &p : vec2)
        cout << p.first << ":" << p.second << endl;
    cout<<"vec3:";
    for (const auto &p : vec3)
        cout << p.first << ":" << p.second << endl;
}

測試:

Bob 16
^Z
vec :Bob:16
vec1:Bob:16
vec2:Bob:16
vec3:Bob:16

關於emplace_back

練習題11.14

擴展11.2.1節練習中編寫的孩子姓到名的map,添加一個pair的vector,保存孩子的名和生日。

#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
class Families
{
public:
    using Child     = pair<string, string>;
    using Children  = vector<Child>;
    using Data      = map<string, Children>;
    void add(string const& last_name, string const& first_name, string birthday)
    {
        auto child = make_pair(first_name, birthday);
        _data[last_name].push_back(child);
    }
    void print() const
    {
        for (auto const& pair : _data)
        {
            cout << pair.first << ":\n" ;
            for (auto const& child : pair.second)
                cout << child.first << " " << child.second << endl;
            cout << endl;
        }
    }
private:
    Data _data;
};
int main()
{
    Families families;
    auto msg = "Please enter last name, first name and birthday:\n";
    for (string l, f, b; cout << msg, cin >> l >> f >> b; families.add(l, f, b));
    families.print();

    return 0;
}

測試:

Please enter last name, first name and birthday:
Bob Jack 95.02.08
Please enter last name, first name and birthday:
Bob Rose 02.08.25
Please enter last name, first name and birthday:
Chen Druk 99.05.20
Please enter last name, first name and birthday:
^Z
Bob:
Jack 95.02.08
Rose 02.08.25

Chen:
Druk 99.05.20

練習題11.15

對一個int到vector的map,其mapped_type、key_type和value_type分別是什麼?

P381表11.3。對於map<int, vector> 類型,mapped_type是vector類型。key_type是int類型。value_type是pair<int, vector>類型

練習題11.16

使用一個map迭代器編寫一個表達式,將一個值賦予一個元素。

#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
int main()
{
    map<string, size_t> word;
    word["test"] = 10;
    map<string, size_t>::iterator map_it = word.begin();

    for(const auto &w:word)
        cout<<w.first<<" "<<w.second<<endl;
    //map_it->first = "train";//錯誤用法 const string不能修改
    map_it->second = 5;
    for(const auto &w:word)
        cout<<w.first<<" "<<w.second<<endl;
    return 0;
}

測試:

test 10
test 5

練習題11.17

假定c是一個string的multiset,v是一個string的vector,解釋下面的調用。指出每個調用是否合法:

(a)copy(v.begin(), v.end(), inserter(c, c.end()));將v中的元素拷貝到c中,使用合法,可以使用inserter將關聯容器當作一個目的位置。
(b)copy(v.begin(), v.end(), back_inserter(c)); 將v中的元素拷貝到c中,但不合法,因爲multiset沒有push_back方法,不能調用back_inserter
(c)copy(c.begin(), c.end(), inserter(v, v.end()));將c中的元素拷貝到v中,使用合法,vector可以使用inserter。
(d)copy(c.begin(), c.end(), back_inserter(v)); 將c中的元素拷貝到v中,使用合法,vector有push_back方法,可以使用back_inserte。

練習題11.18

寫出382頁循環中map_it的類型,不要使用auto或decltype。

爲:map<string, size_t>::const_iterator map_it = word.cbegin();

練習題11.19

定義一個變量,通過11.2.2節中的名爲bookstore的multiset調用begin()來初始化這個變量。寫出變量的類型,不要使用auto或decltype。

using compareType = bool (*)(const Sales_data &lhs, const Sales_data &rhs);
std::multiset<Sales_data, compareType> bookstore(compareIsbn);
std::multiset<Sales_data, compareType>::iterator c_it = bookstore.begin();

練習題11.20

重寫11.1節練習的單詞計數程序,使用insert代替下標操作。你認爲哪個程序更容易編寫和閱讀?解釋原因。

#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
    map<string, size_t> counts;
    for(string word; cin >> word;)
    {
        auto result = counts.insert({ word, 1 });
        if(!result.second)
            ++result.first->second;
    }
    for(auto const& count : counts)
        cout << count.first << " " << count.second << ((count.second > 1) ? " times\n" : " time\n");
}

測試:

test
train
do
test
train
train
^Z
do 1 time
test 2 times
train 3 times

練習題11.21

假定word_count是一個string到size_t的map,word是一個string,解釋下面循環的作用。

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

向word_count中插入元素,如果word已存在,則解引用first迭代器,取值的部分++。如果word不存在,則插入(word,0),載解引用first迭代器,取值的部分++。和上題計數類似。

練習題11.22

給定一個map<string, vector>, 對此容器的插入一個元素的insert版本,寫出其參數類型和返回類型。

參數類型:pair<string, vector<int>>
返回值類型:pair<map<string,vector<int>>::iterator, bool>

練習題11.23

11.2.1節練習中的map以孩子的姓爲關鍵字,保存他們的名的vector,用multimap重寫此map。

#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
    multimap<string, string> families;
    for (string lname, cname; cin >> cname >> lname; families.emplace(lname, cname));
    for (auto const& family : families)
        cout << family.second << " " << family.first << endl;
}

測試:

Jack Rose
Jack Chen
Bob Bush
^Z
Bob Bush
Jack Chen
Jack Rose

練習題11.24

下面的程序完成什麼功能?

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

在m中添加一個關鍵字0,將值“1”賦給它。

練習題11.25

對比下面程序與上一題程序

vector<int> v;
v[0] = 1;

將值“1” 放在vector v中的第一個位置(索引爲0)。

練習題11.26

可以用什麼類型來對一個map進行下標操作?下標運算符返回的類型是什麼?請給出一個具體例子 – 即,定義一個map,然後寫出一個可以用來對map進行下標操作的類型以及下標運算符會返回的類型。

#include <iostream>
#include <map>
#include <string>
#include <typeinfo>
using namespace std;
int main()
{
    map<int, string> m = { { 1,"test" },{ 2,"train" } };
    using KeyType = map<int, string>::key_type;
    cout << "map類型下標: " << typeid(KeyType).name() << endl;
    cout << "從map下標操作符返回: " << typeid(decltype(m[1])).name() << endl;

    return 0;
}

測試:

map類型下標: i
從map下標操作符返回: Ss

練習題11.27

對於什麼問題你會使用count來解決?什麼時候你又會選擇find呢?

P389。

  • 使用find:只關心一個特定的元素是否在容器中。
  • 使用count:在允許重複關鍵字的容器中,如果要對某個特定的元素進行計數。

練習題11.28

對於一個string到int的vector的map,定義並初始化一個變量來保存在其上調用find所返回的結果。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
    map<string, vector<int>> vmap { { "test",{ 1,2,3 } },{ "train",{ 4,5,6 } }};
    map<string, vector<int>>::iterator iter;
    iter = vmap.find("test");
    cout<<((iter != vmap.end() )? "find" : "not find");
    return 0;
}

測試:find

練習題11.29

如果給定的關鍵字不在容器中,upper_bound、lower_bound和equal_range分別會返回什麼?

P390。
如果給定的關鍵字不在容器中, 則upper_bound和lower_bound都返回關鍵字的第一個安全插入點 – 不影響容器中元素順序的插入位置。equal_range返回的兩個迭代器都指向關鍵字可以插入的位置。

練習題11.30

對於本節最後一個程序中的輸出表達式,解釋運算對象pos.first->second的含義

pos.first->second意爲給定關鍵字返回的第一個迭代器的value值。P391。

練習題11.31

編寫程序,定義一個作者及其作品的multimap。使用find在multimap中查找一個元素並用erase刪除它。確保你的程序在元素不在map中時也能正常運行。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
    multimap<string, string> auth_map = {
        {"Andrew", "AI"},
        {"Joe", "science"},
        {"Andrew", "ML"},
        {"Angle", "design"},
        {"Andrew", "NN"}
    };

    string auth_name = "Andrew";
    string work = "NN";
    auto iter = auth_map.find(auth_name);
    auto count = auth_map.count(auth_name);

    while (count) {
        if (iter->second == work) {
            auth_map.erase(iter);
            break;
        }
        else {
            ++ iter;
            -- count ;
        }
    }
    for (auto i : auth_map) {
        cout << i.first << " " << i.second << endl;
    }
    return 0;
}

測試:

Andrew AI
Andrew ML
Angle design
Joe science

練習題11.32

使用上一題定義的multimap編寫一個程序,按字典序打印作者列表和他們的作品。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
int main()
{
    multimap<string, string> auth_map = {
        {"Andrew", "AI"},
        {"Joe", "science"},
        {"Andrew", "ML"},
        {"Angle", "design"},
        {"Andrew", "NN"}
    };
    map <string, multiset<string>> order_auth;
    for (auto& auth : auth_map) {
        order_auth[auth.first].insert(auth.second);
    }
    for (const auto &author : order_auth) {
        cout << author.first << ": ";
        for (const auto &work : author.second)
            cout << work << " ";
        cout << endl;
    }
    return 0;
}

測試:

Andrew: AI ML NN
Angle: design
Joe: science

練習題11.33

實現你自己版本的單詞轉換程序。

#include <map>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
map<string, string> buildMap(ifstream& map_file)
{
    map<string, string> trans_map;
    string key, value;

    while (map_file >> key && getline(map_file, value)) {
        if (value.size() > 1) {
            trans_map[key] = value.substr(1);           // 去掉value前的空格
        }
        else {
            throw runtime_error("no rules for " + key);
        }
    }
    return trans_map;
}

const string& transform(const string& s, const map<string, string>& m)
{
    auto map_iter = m.find(s);

    if (map_iter != m.end()) {
        return map_iter->second;
    }
    // if find the word in map, return the phrase. else return the origin word.
    else {
        return s;
    }
}

void wordTransform(ifstream& map_file, ifstream& input)
{
    auto trans_map = buildMap(map_file);
    string text;

    while (getline(input, text)) {
        istringstream stream(text);
        string word;
        bool first_word = true;

        while (stream >> word) {
            if (first_word) {
                first_word = false;
            }
            else {
                cout << " ";
            }
            cout << transform(word, trans_map);
        }
        cout << endl;
    }
}
int main()
{
    string transform_file = "transform_file.txt";
    string input_file = "input_file.txt";
    ifstream trans(transform_file);
    ifstream input (input_file);

    if (trans && input) {
        wordTransform (trans, input);
    }
    else {
        cout << "open file error!" << endl;
    }
    return 0;
}

測試:

whrere are you
why dont you send me a picture
okay? thanks! later

練習題11.34

如果你將transform函數中的find替換爲下標運算符,會發生什麼情況

若使用下標運算符,如果轉換map中要查找的key不存在,則會直接插入一條新的數據,value爲空。

練習題11.35

在buildMap中,如果進行如下改寫,會有什麼效果?
trans_map[key] = value.substr(1);
改爲trans_map.insert({key, value.substr(1)})

如果轉換文件中有重複key,若使用下標進行插入,則value是最後文件中key對應的最後一個短語。而如果使用insert,則key對應的是第一個短語。

練習題11.36

我們的程序並沒有檢查輸入文件的合法性,特別是,它假定轉換規則文件中的規則都是有意義的。如果文件中的某一行包含一個關鍵字、一個空格,然後就結束了,會發生什麼?預測程序的行爲並進行驗證,再與你的程序進行比較。

報錯:

terminate called after throwing an instance of 'std::runtime_error'    
what():  no rules for brb   

練習題11.37

一個無序容器與其有序版本相比有何優勢?有序版本有何優勢?

P394。
有序容器的特點:

  • 以順序遍歷元素,順序可規定,默認情況下使用"<"進行排序。
  • 主要使用排序vector,二叉搜索樹實現。
  • 查找元素操作的時間複雜度爲O(logn)。
  • 插入、移動元素操作的時間複雜度更低,最大爲O(logn)。

無序容器的特點:

  • 遍歷元素沒有特定的順序。
  • 如果hash策略良好,則搜索、插入、移動操作的時間複雜度都是常數。
  • 主要使用哈希實現。

練習題11.38

用unordered_map重寫單詞計數程序和單詞轉換程序。

#include <unordered_map>
#include <set>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

void wordCounting()
{
    unordered_map<string, size_t> word_count;
    for (string word; cin >> word; ++word_count[word]);
    for (const auto &w : word_count)
        cout << w.first << " occurs " << w.second << (w.second > 1 ? "times" : "time") << endl;
}

void wordTransformation()
{
    ifstream ifs_map("transform_file.txt"), ifs_content("input_file.txt");
    if (!ifs_map || !ifs_content) {
        cerr << "can't find the documents." << endl;
        return;
    }

    unordered_map<string, string> trans_map;
    for (string key, value; ifs_map >> key && getline(ifs_map, value); )
        if (value.size() > 1) trans_map[key] = value.substr(1).substr(0, value.find_last_not_of(' '));

    for (string text, word; getline(ifs_content, text); cout << endl)
        for (istringstream iss(text); iss >> word; ) {
            auto map_it = trans_map.find(word);
            cout << (map_it == trans_map.cend() ? word : map_it->second) << " ";
        }
}

int main()
{
    //wordCounting();
    wordTransformation();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章