C++Primer 第十章 泛型算法

10.1 概述

10.1 头文件 algorithm 中定义了一个名为 count 的函数,它类似 find, 接受一对迭代器和一个值作为参数。 count 返回给定值在序列中出现的次数。编写程序,读取 int 序列存入vector中,打印有多少个元素的值等于给定值。
10.2 重做上一题,但读取 string 序列存入 list 中。

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <list>

using namespace std;

int main()
{
   vector<int> vi{1,2,3,1,4,5,7,1,9};
   cout << count(vi.begin(), vi.end(), 1) << endl;

   list<string> lst{"czy", "chen", "zi", "czy"};
   cout << count(lst.begin(), lst.end(), "czy") << endl;

   return 0;
}

10.2 初始泛型算法

10.2.1 只读算法

10.3 用 accumulate求一个 vector 中元素之和。

#include <iostream>
#include <string>
#include <numeric>
#include <vector>

using namespace std;

int main()
{
    vector<int> vi{1, 2, 3, 4, 5, 6, 7, 8, 9};
    cout << accumulate(vi.cbegin(), vi.cend(), 0) << endl;

    return 0;
}

10.4 假定 v 是一个vector,那么调用 accumulate(v.cbegin(),v.cend(),0) 有何错误(如果存在的话)?

结果默认是int类型的,精度会降低。

10.5 在本节对名册(roster)调用equal 的例子中,如果两个名册中保存的都是C风格字符串而不是string,会发生什么?

equal会比较指针地址,而不是字符串值,比较的结果与string类型的不一致。

10.2.2 写容器的算法

10.6 编写程序,使用 fill_n 将一个序列中的 int 值都设置为 0。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    vector<int> vi{1, 2, 3, 4};
    fill_n(vi.begin(), vi.size(), 0);

    for(const auto i : vi)
        cout << i << " ";
    cout << endl;

    return 0;
}

10.7 下面程序是否有错误?如果有,请改正

(a) vector<int> vec; list<int> lst; int i;
	while (cin >> i)
		lst.push_back(i);
	copy(lst.cbegin(), lst.cend(), vec.begin());
	// copy(lst.cbegin(), lst.cend(), back_inserter(ve));
(b) vector<int> vec;
	vec.reserve(10);
	// vec.resize(10);
	fill_n(vec.begin(), 10, 0);

10.8 本节提到过,标准库算法不会改变它们所操作的容器的大小。为什么使用 back_inserter 不会使这一断言失效?

back_inserter是的一部分,而不是。

10.2.3 重排容器元素的算法

10.9 实现你自己的 elimDups。分别在读取输入后、调用 unique后以及调用erase后打印vector的内容。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    vector<string> vec;
    string s;
    while (cin >> s)
        vec.push_back(s);

    sort(vec.begin(), vec.end());
    auto end_unique = unique(vec.begin(), vec.end());
    vec.erase(end_unique, vec.end());

    for (const auto s : vec)
        cout << s << " ";
    cout << endl;

    return 0;
}

10.10 你认为算法不改变容器大小的原因是什么?

改变容器大小可能会导致迭代器失效,而且每种容器都有自己的特性,改变容器大小可能需要使用不同的方法,不改变容器大小使得算法更加通用。

10.3 定制操作

10.11 编写程序,使用 stable_sort 和 isShorter 将传递给你的 elimDups 版本的 vector 排序。打印 vector的内容,验证你的程序的正确性。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

void elimDups(vector<string> &word)
{
    stable_sort(word.begin(), word.end(), isShorter);
    auto end_unique = unique(word.begin(), word.end());
    word.erase(end_unique, word.end());
}

int main()
{
    vector<string> word;
    string s;
    while (cin >> s)
        word.push_back(s);

    elimDups(word);

    for (const auto s : word)
        cout << s << " ";
    cout << endl;

    return 0;
}

10.12 编写名为 compareIsbn 的函数,比较两个 Sales_data 对象的isbn() 成员。使用这个函数排序一个保存 Sales_data 对象的 vector。

#include <iostream>
#include <string>
#include <algorithm>
#include "Sales_data.h"

using namespace std;

bool compareSales(const Sales_data &s1, const Sales_data &s2)
{
    return s1.isbn() < s2.isbn();
}

void compareIsbn(vector<Sales_data> &vs)
{
    sort(vs.begin(), vs.end(), compareSales);
}

int main()
{
    vector<Sales_data> vs;
    Sales_data s;
    while (read(cin, s))
        vs.push_back(s);

    compareIsbn(vs);

    for (const auto &s : vs)
        print(cout, s) << endl;
           

    return 0;
}

10.13 标准库定义了名为 partition 的算法,它接受一个谓词,对容器内容进行划分,使得谓词为true 的值会排在容器的前半部分,而使得谓词为 false 的值会排在后半部分。算法返回一个迭代器,指向最后一个使谓词为 true 的元素之后的位置。编写函数,接受一个 string,返回一个 bool 值,指出 string 是否有5个或更多字符。使用此函数划分 words。打印出长度大于等于5的元素。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

bool gre_words(string &s)
{
    return s.size() >= 5;
}

int main()
{
    vector<string> words;
    string s;
    
    while (cin >> s)
        words.push_back(s);
    auto it = partition(words.begin(), words.end(), gre_words);
    words.erase(it, words.end());

    for (const auto &s : words)
        cout << s << " ";
    cout << endl;

    return 0;
}

10.3.2 lambda表达式

10.14 编写一个 lambda ,接受两个int,返回它们的和
10.15 编写一个 lambda ,捕获它所在函数的 int,并接受一个 int参数。lambda 应该返回捕获的 int 和 int 参数的和。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

void func(int a)
{
    auto add_int1 = [a] (int b) {return a + b;};
    cout << add_int1(2) << endl;
}

int main()
{
    auto add_int = [](int a, int b) { return a + b; };
    cout << add_int(1, 2) << endl;

    func(1);

    return 0;
}

10.16 使用 lambda 编写你自己版本的 biggies。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

void elimDups(vector<string> &word)
{
    stable_sort(word.begin(), word.end(), isShorter);
    auto end_unique = unique(word.begin(), word.end());
    word.erase(end_unique, word.end());
}

void biggies(vector<string> &words, vector<string>::size_type sz)
{
    elimDups(words);
    stable_sort(words.begin(), words.end(), 
            [](const string &s1, const string &s2){return s1.size() < s2.size();});
    
    auto wc = find_if(words.begin(), words.end(), 
                [sz](const string s){return s.size() >= sz;});
            
    for_each(wc, words.end(), [](const string &s){cout << s << " ";});
    cout << endl;
}

int main()
{
    vector<string> words{"abcd", "chenziy", "wangcheng", "xiang", "yun"};
    biggies(words, 4);

    return 0;
}

10.17 重写10.3.1节练习10.12的程序,在对sort的调用中使用 lambda 来代替函数 compareIsbn。

#include <iostream>
#include <string>
#include <algorithm>
#include "Sales_data.h"

using namespace std;

// bool compareSales(const Sales_data &s1, const Sales_data &s2)
// {
//     return s1.isbn() < s2.isbn();
// }

void compareIsbn(vector<Sales_data> &vs)
{
    sort(vs.begin(), vs.end(), 
        [](const Sales_data &s1, const Sales_data &s2){return s1.isbn() < s2.isbn();});
}

int main()
{
    vector<Sales_data> vs;
    Sales_data s;
    while (read(cin, s))
        vs.push_back(s);

    compareIsbn(vs);

    for (const auto &s : vs)
        print(cout, s) << endl;
           

    return 0;
}

10.18 重写 biggies,用 partition 代替 find_if。我们在10.3.1节练习10.13中介绍了 partition 算法。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

void elimDups(vector<string> &word)
{
    stable_sort(word.begin(), word.end(), isShorter);
    auto end_unique = unique(word.begin(), word.end());
    word.erase(end_unique, word.end());
}

void biggies(vector<string> &words, vector<string>::size_type sz)
{
    elimDups(words);
    // stable_sort(words.begin(), words.end(), 
    //         [](const string &s1, const string &s2){return s1.size() < s2.size();});
    
    // auto wc = find_if(words.begin(), words.end(), 
    //             [sz](const string s){return s.size() >= sz;});

    auto wc = partition(words.begin(), words.end(), 
        [sz](const string s){return s.size() >= sz;});
            
    for_each(words.begin(), wc, [](const string &s){cout << s << " ";});
    cout << endl;
}

int main()
{
    vector<string> words{"abcd", "chenziy", "wangcheng", "xiang", "yun"};
    biggies(words, 5);

    return 0;
}

10.19 用 stable_partition 重写前一题的程序,与 stable_sort 类似,在划分后的序列中维持原有元素的顺序。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

void elimDups(vector<string> &word)
{
    stable_sort(word.begin(), word.end(), isShorter);
    auto end_unique = unique(word.begin(), word.end());
    word.erase(end_unique, word.end());
}

void biggies(vector<string> &words, vector<string>::size_type sz)
{
    elimDups(words);
    // stable_sort(words.begin(), words.end(), 
    //         [](const string &s1, const string &s2){return s1.size() < s2.size();});
    
    // auto wc = find_if(words.begin(), words.end(), 
    //             [sz](const string s){return s.size() >= sz;});

    auto wc = stable_partition(words.begin(), words.end(), 
        [sz](const string s){return s.size() >= sz;});
            
    for_each(words.begin(), wc, [](const string &s){cout << s << " ";});
    cout << endl;
}

int main()
{
    vector<string> words{"abcd", "chenziy", "wangcheng", "xiang", "yun", "xingbu"};
    biggies(words, 5);

    return 0;
}

10.3.3 lambda捕捉和返回

10.20 标准库定义了一个名为 count_if 的算法。类似 find_if,此函数接受一对迭代器,表示一个输入范围,还接受一个谓词,会对输入范围中每个元素执行。count_if返回一个计数值,表示谓词有多少次为真。使用count_if重写我们程序中统计有多少单词长度超过6的部分。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    vector<string> vs{"absssscds", "asdedfg", "zxcvvv", "czy", "wcx"};

    int count = count_if(vs.begin(), vs.end(), 
                        [](const string &s){return s.size() > 6;});
    
    cout << count << endl;

    return 0;
}

10.21 编写一个 lambda,捕获一个局部 int 变量,并递减变量值,直至它变为0。一旦变量变为0,再调用lambda应该不再递减变量。lambda应该返回一个bool值,指出捕获的变量是否为0。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    int i = 1;
    auto f = [&i]() -> bool
    {
        if(i > 0) return i--;
        else return 0;
    };

    cout << f() << endl;
    cout << f() << endl;

    return 0;
}

10.3.4 参数绑定

10.23 重写统计长度小于等于6的单词数量的程序,使用函数代替 lambda。

#include <iostream>
#include <string>
#include <algorithm>
#include <functional>

using namespace std;
using namespace std::placeholders;

bool bigger(const string &s, int sz)
{
    return s.size() > sz;
}

int count(const vector<string> &vs, int sz)
{
    return count_if(vs.begin(), vs.end(), bind(bigger, _1, sz));
}

int main()
{
    vector<string> vs{"absssscds", "asdedfg", "zxcvvv", "czy", "wcx"};

    cout << count(vs, 6) << endl;

    return 0;
}

10.23 bind 接受几个参数?

假设要绑定的函数有n个参数,绑定取n + 1个参数。另外一个是函数本身的绑定。

10.24 给定一个string,使用 bind 和 check_size 在一个 int 的vector 中查找第一个大于string长度的值。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;
using namespace std::placeholders;

bool check_size(int sz, const string &s)
{
    return sz > s.size();
}

int main()
{
    vector<int> vi{1, 3, 2, 5, 4};
    string s = "ac";

    auto it = find_if(vi.begin(), vi.end(), bind(check_size, _1, s));
    cout << *it << endl;

    return 0;
}

10.25 在10.3.2节的练习中,编写了一个使用partition 的biggies版本。使用 check_size 和 bind 重写此函数。

#include <iostream>
#include <string>
#include <algorithm>
#include <functional>

using namespace std;
using namespace std::placeholders;

bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

void elimDups(vector<string> &word)
{
    stable_sort(word.begin(), word.end(), isShorter);
    auto end_unique = unique(word.begin(), word.end());
    word.erase(end_unique, word.end());
}

bool check_size(const string &s, int sz)
{
    return sz < s.size();
}

void biggies(vector<string> &words, vector<string>::size_type sz)
{
    elimDups(words);

    auto wc = stable_partition(words.begin(), words.end(), 
        bind(check_size, _1, sz));
            
    for_each(words.begin(), wc, [](const string &s){cout << s << " ";});
    cout << endl;
}

int main()
{
    vector<string> words{"abcd", "chenziy", "wangcheng", "xiang", "yun", "xingbu"};
    biggies(words, 5);

    return 0;
}

10.4 再探迭代器

10.4.1 插入迭代器

10.26 解释三种迭代器的不同之处。

back_inserter创建一个使用push_back的迭代器;
front_inserter创建一个使用push_front的迭代器;
inserter创建一个使用insert的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。

10.27 除了 unique 之外,标准库还定义了名为 unique_copy 的函数,它接受第三个迭代器,表示拷贝不重复元素的目的位置。编写一个程序,使用 unique_copy将一个vector中不重复的元素拷贝到一个初始化为空的list中。

#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>

using namespace std;

int main()
{
    vector<int> vi{1, 2, 2, 6, 7, 3};
    list<int> li;

    unique_copy(vi.begin(), vi.end(), front_inserter(li));

    for (auto i : li)
        cout << i << " ";
    cout << endl;

    return 0;
}

10.28 一个vector 中保存 1 到 9,将其拷贝到三个其他容器中。分别使用inserter、back_inserter 和 front_inserter 将元素添加到三个容器中。对每种 inserter,估计输出序列是怎样的,运行程序验证你的估计是否正确。

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    vector<int> vi{1, 2, 3, 4, 5, 6, 7, 8, 9};
    list<int> v1, v2, v3;

    copy(vi.begin(), vi.end(), inserter(v1, v1.begin()));
    for (auto i : v1)
        cout << i << " ";
    cout << endl;

    copy(vi.begin(), vi.end(), front_inserter(v2));
    for (auto i : v2)
        cout << i << " ";
    cout << endl;

    copy(vi.begin(), vi.end(), back_inserter(v3));
    for (auto i : v3)
        cout << i << " ";
    cout << endl;

    return 0;
}

10.4.2 iostream迭代器

10.29 编写程序,使用流迭代器读取一个文本文件,存入一个vector中的string里。

#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <algorithm>

using namespace std;

int main()
{
    ifstream in("file1");
    istream_iterator<string> item_iter(in), eof;

    vector<string> vec(item_iter, eof);
    for (auto s : vec)
        cout << s << " ";
    cout << endl;


    return 0;
}

10.30 使用流迭代器、sort 和 copy 从标准输入读取一个整数序列,将其排序,并将结果写到标准输出。
10.31 修改前一题的程序,使其只打印不重复的元素。你的程序应该使用 unique_copy。

#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>

using namespace std;

int main()
{
    istream_iterator<int> item_iter(cin), eof;
    ostream_iterator<int> out_iter(cout, " ");

    vector<int> vec(item_iter, eof);
    sort(vec.begin(), vec.end());
    // copy(vec.begin(), vec.end(), out_iter);
    unique_copy(vec.begin(), vec.end(), out_iter);

    return 0;
}

10.32 重写1.6节中的书店程序,使用一个vector保存交易记录,使用不同算法完成处理。使用 sort 和10.3.1节中的 compareIsbn 函数来排序交易记录,然后使用 find 和 accumulate 求和。

#include "Sales_data.h"
#include <iterator>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    istream_iterator<Sales_data> item_iter(cin), eof;
    Sales_data s;

    vector<Sales_data> vec(item_iter, eof);
    sort(vec.begin(), vec.end(), 
                [](const Sales_data &s1, const Sales_data &s2) {return s1.isbn() < s2.isbn();});

    for (auto iter = vec.begin(); iter != vec.end(); ++iter)
    {
        auto end = find_if(iter, vec.end(), [iter](const Sales_data &s) {return s.isbn() != iter->isbn();});
        cout << accumulate (iter, end, s.isbn()) << endl;
    }

    return 0;
}

10.33 编写程序,接受三个参数:一个输入文件和两个输出文件的文件名。输入文件保存的应该是整数。使用 istream_iterator 读取输入文件。使用 ostream_iterator 将奇数写入第一个输入文件,每个值后面都跟一个空格。将偶数写入第二个输出文件,每个值都独占一行。

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    ifstream in("file1");
    ofstream out1("file2"), out2("file3");

    istream_iterator<int> item_iter(in), eof;
    ostream_iterator<int> out_iter1(out1, " "), out_iter2(out2, "\n");

    vector<int> vec(item_iter, eof);
    for (auto i : vec)
    {
        i % 2 ? out_iter1 = i : out_iter2 = i;
        cout << i << " ";
    }      
    cout << endl;
    
    return 0;
}

10.4.3 反向迭代器

10.34 使用 reverse_iterator 逆序打印一个vector。

#include <iostream>
#include <string>
#include <vector>
#include <iterator>

using namespace std;

int main()
{
    vector<int> vi{1, 2, 3, 4, 5};
    for (auto r_iter = vi.crbegin(); r_iter != vi.crend(); ++r_iter)
        cout << *r_iter << " ";
    cout << endl;

    return 0;
}

10.35 使用普通迭代器逆序打印一个vector。

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main()
{
    vector<int> vi{1, 2, 3, 4};
    for (auto iter = vi.cend(); iter != vi.cbegin(); )
        cout << *(--iter) << " ";
    cout << endl;
         

    return 0;
}

10.36 使用 find 在一个 int 的list 中查找最后一个值为0的元素。

#include <iostream>
#include <string>
#include <algorithm>
#include <list>

using namespace std;

int main()
{
    list<int> li{0, 1, 2, 3, 0};
    auto r_iter = find(li.crbegin(), li.crend(), 0);
    cout << *r_iter << endl;

    return 0;
}

10.37 给定一个包含10 个元素的vector,将位置3到7之间的元素按逆序拷贝到一个list中。

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    vector<int> vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    list<int> li;

    copy(vi.crbegin()+3, vi.crend()-2, back_inserter(li));
    for (auto i : li)
        cout << i << " ";
    cout << endl;

    return 0;
}

10.5 泛型算法结构

10.5.1 5类迭代器

10.38 列出5个迭代器类别,以及每类迭代器所支持的操作。

输入迭代器:==!=++、、->;
输出迭代器:++、;
前项迭代器:==!=++、、->;
双向迭代器:==!=++、–、、->;
随机访问迭代器:==!=++、–、、-><<=>>=++=--=-、iter[n](iter[n])

10.39 list 上的迭代器属于哪类?vector呢?

list属于双向迭代器;vector属于随机访问迭代器。

10.40 你认为 copy 要求哪类迭代器?reverse 和 unique 呢?

copy:前两个参数为输入迭代器,第三个参数为输出迭代器;
reverse:双向迭代器;
unique:前向迭代器。

10.5.2 算法形参模式

10.5.3 算法命名规范

10.41仅根据算法和参数的名字,描述下面每个标准库算法执行什么操作:

replace(beg, end, old_val, new_val); // 在beg与end之间,如果是old_val则替换为new_val
replace_if(beg, end, pred, new_val); // 在beg与end之间,如果满足谓词条件则替换为new_val
replace_copy(beg, end, dest, old_val, new_val); // 在beg与end之间,如果是old_val则替换为new_val,不改变原始值,将结果复制到dest
replace_copy_if(beg, end, dest, pred, new_val); // 在beg与end之间,如果满足谓词条件则替换为new_val,不改变原始值,将结果复制到dest

10.6 特定容器算法

10.42 使用 list 代替 vector 重新实现10.2.3节中的去除重复单词的程序。

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <algorithm>

using namespace std;

void elimDups(list<string> &word)
{
    word.sort();
    word.unique();
}

int main()
{
    list<string> word;
    string s;
    while (cin >> s)
        word.push_back(s);

    elimDups(word);

    for (const auto s : word)
        cout << s << " ";
    cout << endl;

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值