STL算法积累(二)

C++ sort 
stable_sort
partial_sort 
nth_element 
is_sorted 
is_sorted_until
merge
inplace_merge
copy_n
copy_if
copy_backward
reverse
unique/unique_copy
move
remove、remove_copy、remove_if和remove_copy_if
rotate
rotate_copy
fill和fill_n

[1]C++ sort(STL sort)排序算法详解  //默认升序
1.1 在很多应用中,排序都是至关重要的,而且很多 STL 算法也只适用于有序对象序列。定义在 algorithm 头文件中的函数模板 sort<Iter>() 默认会将元素段排成升序,这也就意味着
排序的对象的类型需要支持 < 运算符。 
1.2 对象也必须是可交换的,这说明可以用定义在 utility 头文件中的函数模板 swap() 来对两个对象进行交换。这进一步表明这种对象的类型需要实现移动构造函数和移动赋值运算符。
1.3 函数模板 sort<Iter>() 的类型参数 Iter 是元素段元素对应的迭代器类型,而且它们必须支持随机访问迭代器。这表明 sort() 算法只能对提供随机访问迭代器的容器中的元素进行排序,
也说明 sort() 只能接受 array、vector、deque 或标准数组中的元素。可以回顾前面章节,list 和 forward_list 容器都有成员函数 sort(); 这些用来排序的特殊成员函数是必要的,
因为 list 只提供双向迭代器,且 forward_list 只提供正向迭代器。

    可以从函数调用的参数中推导出 sort() 的模板类型参数,它们是定义被排序对象范围的迭代器。当然,迭代器类型隐式定义了这个元素段中对象的类型。下面是一个使用 sort() 算法的示例:
std::vector<int> numbers {99, 77, 33, 66, 22, 11, 44, 88};
std::sort(std::begin(numbers), std::end(numbers));
std::copy(std::begin(numbers), std::end(numbers),std:: ostream_iterator<int> {std::cout," "}) ;
// Output: 11 22 33 44 66 77 88 99

sort() 调用将 number 容器的全部元素排成升序,然后用 copy() 算法输出结果。可以不必对容器的全部内容进行排序。下面这条语句对 numbers 中除了第一个和最后一个元素之外的元素进行了排序:
std::sort(++std::begin(numbers),--std::end(numbers));

为了将元素排成降序,需要提供一个用来比较元素的函数对象,作为 sort() 的第三个参数:
std::sort(std::begin(numbers), std::end(numbers), std::greater<>());
这个比较函数必须返回布尔值,而且有两个参数,它们要么是迭代器解引用后得到的类型,要么是迭代器解引用后可以隐式转换成的类型。参数可以是不同类型的。只要比较函数满足这些条件,它就可以是你喜欢的
任何样子,也包括 lambda 表达式。例如:
std::deque<string> words { "one", "two", "nine", "nine", "one", "three", "four", "five", "six" };
std::sort(std::begin(words), std::end(words),[](const string& s1, const string& s2) { return s1.back()> s2.back();});
std::copy(std::begin(words), std::end(words),
std::ostream_iterator<string> {std::cout," "}); // six four two one nine nine one three five
这段代码对 deque 容器 words 中的 string 元素进行了排序,并且输出了排序后的结果。这里的比较函数是一个 lambda 表达式,它们用每个单词的最后一个字母来比较排序的顺序。结果元素以它们最后一
个字母的降序来排序。
//http://c.biancheng.net/view/561.html
std::vector<Name> names;
    std::cout << "Enter names as first name followed by second name. Enter Ctrl+Z to end:";
    std::copy(std::istream_iterator<Name>(std::cin), std::istream_iterator<Name>(),std::back_insert_iterator<std::vector<Name>>(names));
    ......
    names 容器用来保存从 cin 读入的姓名。输入由 copy() 算法执行,它使用一个 istream_iterator<Name> 实例读入 Name 对象。 istream_iterator<Name> 默认的构造函数会创建流的结束迭代器。
copy() 函数用 back_insert_iterator<Name>() 创建的 back_inserter<Name> 迭代器将输入的每个对象复制到 names 中。
    std::cout << names.size() << " names read. Sorting in ascending sequence...\n";
    std::sort(std::begin(names), std::end(names), [](const Name& name1, const Name& name2) {return name1.get_second() < name2.get_second(); });
    std::cout << "\nThe names in ascending sequence are:\n";
    std::copy(std::begin(names), std::end(names), std::ostream_iterator<Name>(std::cout, "\n"));
}
    为 Name 类重载的流运算符允许使用流迭代器来输入和输出 Name 对象。

[2]C++ stable_sort(STL stable_sort)排序算法详解
sort() 算法可能会改变相等元素的顺序,有时候这不是我们想要的。这种情况下,stable_sort() 算法可以满足我们的要求,它会对一段元素进行排序并保证维持相等元素的原始顺序。
为了演示 stable_sort() 的使用,可以对上一节中的 names 容器进行排序的语句进行修改:
std::stable_sort(std::begin(names), std::end(names),[](const Name& name1, const Name& name2) { return name1.get_second() < name2.get_second(); });

[3]C++ partial_sort(STL partial_sort)排序算法详解  [部分排序]
    假设有一个容器,它保存了 100 万个数值,但我们只对其中最小的 100 个感兴趣。可以对容器的全部内容排序,然后选择前 100 个元素,但这可能有点消耗时间。这时候需要使用部分排序,只需要
这些数中的前100个是有序放置的。
   对于部分排序,有一个特殊的算法 partial_sort(),它需要 3 个随机访问迭代器作为参数。如果这个函数的参数是 first、second 和 last,那么这个算法会被应用到 [first,last) 这个范围
内的元素上。执行这个算法后,[first,second) 会包含降序序列 [first,last) 中最小的 second-first 个元素。

   下面这段代码演示了 partial_sort() 算法的工作方式:
size_t count {5}; // Number of elements to be sorted
std::vector<int> numbers {22, 7, 93, 45, 19, 56, 88, 12, 8, 7, 15, 10};
std::partial_sort(std::begin(numbers), std::begin(numbers) + count, std::end(numbers))
程序执行后最小的 count 个元素是有序的。在 [first,second) 范围内,second 指向的元素没有被包含在内,因为 second 是一个开结束点。需要注意的是,不能保持未排序元素的原始顺序。
在执行 partial_sort() 后这些元素的顺序是不确定的,这取决于我们的实现。
3.1 如果想让 partial_sort() 算法使用和 < 运算符不同的比较函数,可以提供一个函数对象作为它的额外参数。例如:
std::partial_sort(std::begin(numbers), std::begin(numbers) + count, std:: end (numbers) , std::greater<>());
现在,number 中最大的 count 个元素会是容器开始处的一个降序序列。在此系统上这条语句的输出结果为:
93 88 56 45 22 7 19 12 8 7 15 10
同样,没有保持 numbers 中未排序元素的原始顺序。
3.2 partial_sort_copy() 在本质上和 partial_sort() 是相同的,除了前者会将排序元素复制到一个不同的元素段(另一个容器中)。它的前两个参数是指定部分排序应用范围的迭代器; 
第 3 个和第 4 个参数是标识结果存放位置的迭代器。目的位置的元素个数决定了输入元素段中被排序元素的个数。例如:
std::vector<int> numbers {22, 7, 93, 45, 19, 56, 88, 12, 8, 7, 15, 10};
size_t count {5}; // Number of elements to be sorted
std::vector<int> result(count); // Destination for the results 一 count elements
std::partial_sort_copy(std::begin(numbers), std::end(numbers), std::begin (result) , std::end (result)); std::copy(std::begin(numbers), std::end(numbers), std::ostream_iterator<int>{std::cout," " });
std::cout << std::endl;
std::copy(std::begin(result), std::end(result),std::ostream_iterator <int> {std::cout, " "}); std::cout << std::endl
这些语句实现了对 numbers 容器的部分排序。这个想法是先对 numbers 中最小的 count 个元素排序,然后把它们保存在结果容器中。我们指定的用于存放元素的目的位置必须存在,也就是说,
目的容器 result 必须至少有 count 个元素,在这个示例中我们需要分配准确数目的内存。执行这段代码后的输出如下:
22 7 93 45 19 56 88 12 8 7 15 10
7 7 8 10 12
可以看到,numbers 中元素的顺序并没有被打乱!!! result 中包含了 number 中按升序排列的最小的 count 个元素。
3.3 当然,也可以用额外的参数来指定不同的比较函数:
std::partial_sort_copy(std::begin(numbers), std::end(numbers), std::begin(result), std::end(result),std::greatero());
指定一个 greater<> 的实例作为函数对象,将最大的 count 个元素以降序的形式复制到 result 中。如果这条语句后跟的是前一个代码段中的输出语句,会输出如下内容:
22 7 93 45 19 56 88 12 8 7 15 10
93 88 56 45 22
同前面一样,原始容器中元素的顺序没有被打乱

[4]C++ nth_element(STL nth_element)排序算法详解
nth_element() 算法和 partial_sort() 不同。应用的范围由它的第一个和第三个参数指定。第二个参数是一个指向第 n 个元素的迭代器。nth_dement() 的执行会导致第 n 个元素被
放置在适当的位置,这个范围内,在第 n 个元素之前的元素都小于第 n 个元素,而且它后面的每个元素都会比它大。算法默认用 < 运算符来生成这个结果。

下面是一个使用 nth_elemen() 的示例:
std::vector<int> numbers {22, 7, 93, 45, 19, 56, 88, 12, 8, 7, 15, 10};
size_t count {5}; // Index of nth element
std::nth_element(std::begin(numbers), std::begin(numbers) + count, std::end(numbers))
第 n 个元素之前的元素都小于它,但不必是有序的。同样,第 n 个元素后的元素都大于它,但也不必是有序的。如果第二个参数和第三个参数相同(元素段的末尾),这时这个算法是无效的。
正如本章前面的算法一样,可以自己定义比较函数作为函数的第 4 个参数:
std::nth_element(std::begin(numbers), std::begin(numbers) + count, std::end(numbers) , std::greater<>());
这里使用 > 运算符来比较函数,所以第 n 个元素将是元素按降序排列后的第 n 个元素。第 n 个元素之前的元素都大于它,之后的元素都小于它。

[5]C++ is_sorted(STL is_sorted)排序算法详解
5.1 排序是要耗费时间的,尤其是在有大量元素时。测试元素段是否已经排好序可以避免不必要的排序操作。如果两个迭代器参数所指定范围内的元素是升序排列的,函数模板 is_sorted() 就会返回 true。
     为了能够顺序处理元素,迭代器至少需要是正向迭代器。提醒一下,正向迭代器支持前缀和后缀形式的自增运算。下面是一个使用 is_sorted() 的示例:
std::vector<int> numbers {22, 7, 93, 45, 19};
std::vector<double> data {1.5, 2.5, 3.5, 4.5};
std::cout << "numbers is "<<(std::is_sorted(std::begin (numbers), std::end(numbers)) ? "": "not ") << "in ascending sequence.\n";
std::cout << "data is "<< (std::is_sorted(std::begin(data), std::end(data)) ?"":"not ") << "in ascending sequence." << std::endl;
     默认使用的比较函数是 < 运算符。鉴于“data is”的输出,表明 numbers 不是一个升序序列。还有一个版本也可以指定用于比较元素的函数对象:
std:: cout << "data reversed is "<< (std::is_sorted(std::rbegin(data), std::rend(data), std::greater<>()) ? "":"not ")<< "in descending sequence." << std::endl;
     这条语句的输出说明 data 中元素的逆序是降序。
5.2 也可用函数模板 is_sorted_until() 来判断一个元素段是否有序。它的参数用来定义要测试的迭代器。这个函数会返回一个指向这段元素中升序序列上边界元素的迭代器。例如:
std::vector<string> pets {"cat", "chicken", "dog", "pig", "llama", "coati", "goat"};
std::cout << "The pets in ascending sequence are:\n";
std::copy(std::begin(pets), std::is_sorted_until(std::begin(pets), std::end (pets)) , std::ostream_iterator<string>{ std::cout," "});
    copy() 算法的前两个参数分别是pets容器的开始迭代器以及当 is_sorted_until() 应用到 pets 中全部元素上时返回的迭代器。is_sorted_until() 算法会返回指向 pets 中升序序列元素的上边界,
它是指向小于其前面元素的第一个元素的迭代器。如果序列是有序的,则返回一个结束迭代器。这段代码的输出如下:
The pets in ascending sequence are:
cat chicken dog pig
"llama"是小于其前者的第一个元素,所以”pig”就是升序序列的最后一个元素

[6]C++ merge和inplace_merge(STL merge和inplace_merge)算法详解
       合并操作会合并两个有相同顺序的序列中的元素,可以是两个升序序列,也可以是两个降序序列。结果会产生一个包含来自这两个输入序列的元素副本的序列,
并且排序方式和原始序列相同。
    merge() 算法会合并两个序列并将结果保存到第三个序列中,它使用 < 运算符来比较元素。merge() 算法需要 5 个参数。其中前两个指定第一个输入序列的迭代器
,后面两个迭代器指定第二个输入序列,最后一个参数是一个指定合并元素存放位置的迭代器。
merge() 算法返回的迭代器指向合并序列末尾的后一个位置,所以可以通过这个函数调用使用的第 5 个参数加上这个函数返回的迭代器来确定合并序列的范围。
6.1 inplace_merge算法
inplace_merge() 算法可以合并同一个序列中两个连续有序的元素序列。它有三个参数: first、second、last 。这个序列中的第一个输入序列
是 [first,second), 第二个输入序列是 [second,last),因而 second 指向的元素在第二个输入序列中。结果为 [first, last)。

[6]C++ copy_n(STL copy_n)算法详解 //copy
    copy_n() 算法可以从源容器复制指定个数的元素到目的容器中。第一个参数是指向第一个源元素的输入迭代器,第二个参数是需要复制的元素的个数,第三个参数是指向目的容器的第一个位置的迭代器。
这个算法会返回一个指向最后一个被复制元素的后一个位置的迭代器。下面是一个使用它的示例:
std::vector<string> names {"A1","Beth", "Carol", "Dan", "Eve","Fred","George" ,"Harry", "Iain", "Joe"};
std::unordered_set<string> more_names {"Janet", "John"};
std::copy_n(std:begin(names)+1, 3, std::inserter(more_names, std::begin(more_names)));
这个 copy_n() 操作会从 names 的第二个元素开始复制 3 个元素到关联容器 more_names 中。目的容器是由一个 unordered_set 容器的 insert_iterator 对象指定的,它是由 inserter()
函数模板生成的。insert_iterator 对象会调用容器的成员函数 insert() 来向容器中添加元素。

当然,copy_n() 的目的地址也可是以流迭代器:
std::copy_n(std::begin(more_names), more_names.size()-1,std::ostream_iterator<string> {std::cout, " "});
这样会输出 more_names 中除了最后一个元素之外的全部元素。注意,如果被复制元素的个数超过了实际元素的个数,程序会因此崩溃。如果元素的个数为 0 或负数,copy_n() 算法什么也不做。

[7]copy_if(STL copy_if)算法详解
    copy_if() 算法可以从源序列复制使谓词返回 true 的元素,所以可以把它看作一个过滤器。前两个参数定义源序列的输入迭代器,第三个参数是指向目的序列的第一个位置的输出迭代器,第 4 个参数是一个谓词。
会返回一个输出迭代器,它指向最后一个被复制元素的下一个位置。下面是一个使用 copy_if() 的示例:
std::vector<string> names {"A1", "Beth", "Carol", "Dan", "Eve","Fred", "George", "Harry", "Iain", "Joe"};
std::unordered_set<string> more_names {"Jean", "John"};
size_t max_length{4};
std::copy_if(std::begin(names), std::end(names), std::inserter(more_names, std::begin(more_names)), [max_length](const string& s) { return s.length() <= max_length;});
    因为作为第 4 个参数的 lambda 表达式所添加的条件,这里的 copy_if() 操作只会复制 names 中的 4 个字符串或更少。目的容器是一个 unordered_set 容器 more_names,它已经包含两个含有 4 个字符的名称。
和前面的章节一样,insert_itemtor 会将元素添加到限定的关联容器中。如果想要展示它是如何工作的,可以用 copy() 算法列出 more_names 的内容:
std::copy(std::begin(more_names), std::end(more_names), std::ostream iterator <string>{std::cout, " "});
std::cout << std::endl;
当然,copy_if() 的目的容器也可以是一个流迭代器:
std::vector<string> names { "Al", "Beth", "Carol", "Dan", "Eve","Fred", "George", "Harry", "Iain", "Joe"};
size_t max_length{4};
std::copy_if(std::begin(names), std::end(names), std::ostream iterator< string> {std::cout," "}, [max_length](const string& s) { return s.length() > max_length; });
std::cout << std::endl;
这里会将 names 容器中包含的含有 4 个以上字符的名称写到标准输出流中。这段代码会输出如下内容:
Carol George Harry
7.1输入流迭代器可以作为 copy_if() 算法的源,也可以将它用在其他需要输入迭代器的算法上。例如:
std::unordered_set<string> names;
size_t max_length {4};
std::cout << "Enter names of less than 5 letters. Enter Ctrl+Z on a separate line to end:\n";
std::copy_if(std::istream_iterator<string>{std::cin},std:: istream iterator<string>{}, std::inserter(names, std::begin (names)),
             [max_length](const string& s) { return s.length() <= max_length; });
std::copy(std::begin(names), std::end(names), std::ostream_iterator <string>{std::cout," "});
std::cout << std::endl;
容器 names 最初是一个空的 unordered_set。只有当从标准输入流读取的姓名的长度小于或等于 4 个字符时,copy_if() 算法才会复制它们。执行这段代码可能会产生如下输出:
Enter names of less than 5 letters. Enter Ctrl+Z on a separate line to end:
Jim Bethany Jean Al Algernon Bill Adwina Ella Frederick Don ^Z
Ella Jim Jean Al Bill Don
超过 5 个字母的姓名可以从 cin 读入,但是被忽略掉,因为在这种情况下第 4 个参数 的判定会返回 false。因此,输入的 10 个姓名里面只有 6 个会被存储在容器中。

[8]copy_backward(STL copy_backward)算法详解//http://c.biancheng.net/view/605.html
不要被 copy_backward() 算法的名称所误导,它不会逆转元素的顺序。它只会像 copy() 那样复制元素,但是从最后一个元素开始直到第一个元素。
    copy_backward() 会复制前两个迭代器参数指定的序列。第三个参数是目的序列的结束迭代器,通过将源序列中的最后一个元素复制到目的序列的结束迭代器之前,源序列会被复制到目的序列中。
copy_backward() 的 3 个参数都必须是可以自增或自减的双向迭代器,这意味着这个算法只能应用到序列容器的序列上。
    copy_backward()从源序列的反向,将每一个元素依次复制到目的序列的前一个元素之前的位置。在进行这个操作之前,目的序列中的元素必须存在,因此目的序列至少要有和源序列一样多的元素,但也可以有更多。
copy_backward() 算法会返回一个指向最后一个被复制元素的迭代器,在目的序列的新位置,它是一个开始迭代器。
8.1 copy_backward() 提供了哪些优势
    一个回答是,在序列重叠时,可以用copy()将元素复制到重叠的目的序列剩下的位置——也就是目的序列第一个元素之前的位置。如果想尝试用 copy() 算法将元素复制到同一个序列的右边,这个操作不会成功,因为
被复制的元素在复制之前会被重写。如果想将它们复制到右边,可以使用 copy_backward(),只要目的序列的结束迭代器在源序列的结束迭代器的右边
    在想将元素复制到右边时,copy() 算法显然不能如我们所愿,因为一些元素在复制之前会被重写。在这种情况下,copy_backward() 可以做到我们想做的事。相反在需要将元素复制到 序列的左边时,
copy() 可以做到,但 copy_backward() 做不到。

[9]C++ reverse_copy(STL reverse_copy)算法详解
    reverse_copy() 算法可以将源序列复制到目的序列中,目的序列中的元素是逆序的。定义源序列的前两个迭代器参数必须是双向迭代器。目的序列由第三个参数指定,它是目的序列的开始迭代器,也是一个输出迭代器。
如果序列是重叠的,函数的行为是未定义的。这个算法会返回一个输出迭代器,它指向目的序列最后一个元素的下一个位置。
下面是一个使用 reverse_copy() 和 copy_if() 的示例:
int main()
{
    while(true)
    {
        string sentence;
        std::cout << "Enter a sentence or Ctrl+Z to end: ";
        std::getline(std::cin, sentence);
        if(std::cin.eof()) break;
        // Copy as long as the characters are alphabetic & convert to upper case
        string only_letters;
        std::copy_if(std::begin(sentence), std::end(sentence), std::back_inserter(only_letters),[](char ch) { return std::isalpha(ch); });
        std::for_each(std::begin(only_letters), std::end(only_letters), [](char& ch) { ch = toupper(ch); });
        // Make a reversed copy
        string reversed;
        std::reverse_copy(std::begin(only_letters), std::end(only_letters), std::back_inserter(reversed));
        std::cout << '"' << sentence << '"'<< (only_letters == reversed ? " is" : " is not") << " a palindrome." << std::endl;
    }
}
   这个程序会检查一条语句(也可以是很多条语句)是否是回文的。回文语句是指正着读或反着读都相同的句子,前提是忽略一些像空格或标点这样的细节。while 使我们可以检查尽可能多的句子。用 getline()
读一条句子到 sentence 中。如果读到 Ctrl+Z,输入流中会设置 1 个EOF标志,它会结束循环。用 copy_if() 将 sentence 中的字母复制到 only_letters。这个 lambda 表达式只在参数是学母时返回 true,
所以其他的任何字符都会被忽略。然后用 back_inserter() 生成的 back_insert_iterator 对象将这些字符追加到 only_letter。
   for_each() 算法会将三个参数指定的函数对象应用到前两个参数定义的序列的元素上,因此这里会将 only_letters 中的字符全部转换为大写。然后用 reverse_copy() 算法生成和 only_letters 的内容相反
的内容。比较 only_letters 和 reversed 来判断输入的语句是否为回文。

9.1 reverse() 算法可以在原地逆序它的两个双向迭代器参数所指定序列的元素。可以如下 所示用它来代替上述程序中的 reverse_copy():
string reversed {only_letters};
std::reverse(std::begin(reversed), std::end(reversed));
这两条语句会替换上述程序中 reversed 的定义和 reverse_copy() 调用。它们生成一个 only_letters 的副本 reversed,然后调用 reverse() 原地逆序 reversed 中的字符序列。

[10]C++ unique(STL unique)算法详解//用来消除连续元素   ---unique_copy
    unique() 算法可以在序列中原地移除重复的元素,这就要求被处理的序列必须是正向迭代器所指定的。在移除重复元素后,它会返回一个正向迭代器作为新序列的
结束迭代器。可以提供一个函数对象作为可选的第三个参数,这个参数会定义一个用来代替 == 比较元素的方法。例如:
std::vector<string> words {"one", "two", "two", "three", "two", "two", "two"};
auto end_iter = std::unique(std::begin(words), std::end(words));
std::copy(std::begin(words), end_iter, std::ostream_iterator<string>{std::cout, " "});
std::cout << std::endl;
这样会通过覆盖来消除 words 中的连续元素。输出为:
one two three two
    当然,没有元素会从输入序列中移除;算法并没有方法去移除元素,因为它并不知道它们的具体上下文。整个序列仍然存在。但是,无法保证新末尾之后的元素的状态;
如果在上面的代码中用 std::end(words) 代替 end_iter 来输出结果,得到的输出如下:
one two three two two two two
    相同个数的元素仍然存在,但新的结束迭代器指向的元素为空字符串;最后两个元素还和之前一样。在你的系统上,可能会有不同的结果。因为这个,在执行
unique() 后,最好按如下方式截断序列:
auto end_iter = std::unique(std::begin(words), std::end(words));
words.erase(end_iter, std::end(words));
std::copy (std::begin (words) , std::end (words) , std::ostream iterator<string> {std::cout, " "});
std::cout << std::endl;
    容器的成员函数 erase() 会移除新的结束迭代器之后的所有元素,因此 end(words) 会返回 end_iter。
    当然,可以将 unique() 运用到字符串中的字符上:
string text {"There's no air in spaaaaaace!"};
text.erase(std::unique(std::begin(text), std::end(text),[](char ch1, char ch2) { return ch1 = ' '&& ch1 = ch2; }), std::end(text));
std::cout << text << std::endl; // Outputs: There's no air in spaaaaaace!
这里使用 unique() 会移除字符串 text 中的连续重复的空格。这段代码会用 unique() 返回的迭代器作为 text 成员函数 erase() 的第一个参数,而且它
会指向被移除的第一个字符。erase() 的第二个参数是 text 的结束迭代器,因此在没有重复元素的新字符串之后的所有字符都会被移除。

[11]《Effective STL》里这些话可能有用处:  
“我们总结一下你的排序选择: 
   ● 如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。 
   ● 如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。 
   ● 如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该
       注意和调用的。 
   ● 如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition。 
   ● 如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和stable_sort。如果你需要partial_sort
       或nth_element提供的效果,你就必须间接完成这个任务,但正如我在上面勾画的,会有很多选择。   
另外,你可以通过把数据放在标准关联容器中的方法以保持在任何时候东西都有序。你也可能会考虑标准非STL容器priority_queue,它也可以总是保持它的元素
有序。

[12]C++ move(STL move)函数使用详解
    move() 算法会将它的前两个输入迭代器参数指定的序列移到第三个参数定义的目的序列的开始位置,第三个参数必须是输出迭代器。这个算法返回的迭代器指向最后一个被移动到目的序列的元素的下一个位置。
这是一个移动操作,因此无法保证在进行这个操作之后,输入序列仍然保持不变;源元素仍然会存在,但它们的值可能不再相同了,因此在移动之后,就不应该再使用它们。如果源序列可以被替换或破坏,就可以
选择使用 move() 算法。如果不想扰乱源序列,可以使用 copy() 算法。下面是一个展示如何使用它的示例:
std::vector<int> srce {1, 2, 3, 4};
std::deque<int> dest {5, 6, 7, 8};
std::move(std::begin(srce), std::end(srce), std::back_inserter(dest));
这里会将 data 的最后 6 个元素移到容器的开头。它能够正常工作是因为目的地址在源序列之外。在移动之后,无法保证最后两个元素的值。这里它们虽然被移除了,但同样可以将它们重置为已知的值一一例如0。

   最后一行中的注释展示了输出结果。当然也可以用 rotate() 算法来代替 move() 移动元素,在这种情况下,我们肯定知道最后两个元素的值。
如果一个移动操作的目的地址位于源序列之内,move() 就无法正常工作,这意味着移动需要从序列的右边开始。原因是一些元素在移动之前会被重写,但 move_backward() 可以正常工作。它的前两个参数指定
了被移动的序列,第三个参数是目的地址的结束迭代器。例如:
std::vector<int> data {1, 2, 3, 4, 5, 6, 7, 8};
std::move(std::begin(data) + 2, std::end(data), std::begin(data));
data.erase(std::end(data) - 2, std::end(data)); // Erase moved elements
std::copy(std::begin (data), std::end(data), std::ostream_iterator<int> {std::cout, " "});
std::cout << std::endl;
// 3, 4, 5, 6, 7, 8
这里使用 deque 容器只是为了换个容器使用。将前 6 个元素向右移动两个位置。在移动操作后,值无法得到保证的元素会被重置为 0。最后一行展示了这个操作的结果。

[13]remove、remove_copy、remove_if和remove_copy_if函数使用详解
    如果不知道具体的场景,即元素保存在什么样的容器中,是不能从序列中移除元素的。因此,“移除元素的”算法也无法做到这一点,它们只会重写被选择的元素或者忽略复制的元素。移除操作不会改变被“移除”元素
的序列的元素个数。有 4 种移除算法:
remove() 可以从它的前两个正向迭代器参数指定的序列中移除和第三个参数相等的对象。基本上每个元素都是通过用它后面的元素覆盖它来实现移除的。它会返回一个指向新的最后一个元素之后的位置的迭代器。
remove_copy() 可以将前两个正向迭代器参数指定的序列中的元素复制到第三个参数指定的目的序列中,并忽略和第 4 个参数相等的元素。它返回一个指向最后一个被复制到目的序列的元素的后一个位置的迭代器。
序列不能是重叠的。
remove_if() 可以从前两个正向迭代器指定的序列中移除能够使作为第三个参数的谓词返回 true 的元素。
remove_copy_if() 可以将前两个正向迭代器参数指定的序列中,能够使作为第 4 个参数的谓词返回 true 的元素,复制到第三个参数指定的目的序列中。它返回一个指向最后一个被复制到目的序列的元素的后
一个位置的迭代器。序列不能是重叠的。
13.1可以按如下方式使用 remove():
std::deque<double> samples {1.5, 2.6, 0.0, 3.1, 0.0, 0.0, 4.1, 0.0, 6.7, 0.0};
samples.erase(std::remove(std::begin(samples), std::end(samples), 0.0), std::end(samples));
std::copy(std::begin(samples),std::end(samples), std::ostream iterator <double> {std::cout," "});
std::cout << std::endl;
// 1.5 2.6 3.1 4.1 6.7
    sample 中不应包含为 0 的物理测量值。remove() 算法会通过左移其他元素来覆盖它们,通过这种方式就可以消除杂乱分布的 0。remove() 返回的迭代器指向通过这个操作得到的新序列的尾部,所以可以
用它作为被删除序列的开始迭代器来调用 samples 的成员函数 erase()。
13.2 如果想保留原始序列,并生成一个移除选定元素之后的副本,可以使用 remove_copy()。 例如:
std::deque<double> samples {1.5, 2.6, 0.0, 3.1, 0.0, 0.0, 4.1, 0.0, 6.7, 0.0}; std::vector<double> edited_samples;
std::remove_copy(std::begin(samples), std::end(samples), std::back_inserter(edited_samples), 0.0);
samples 容器中的非零元素会被复制到 edited_samples 容器中,edited_samples 正好是一个 vector 容器。通过 back_insert_iterator 对象将这些元素添加到 edited_samples,因此这个容器只
包含从 sample 中复制的元素。


[14] rotate(STL rotate)算法详解
   为了理解如何旋转序列,可以将序列中的元素想象成手镯上的珠子。rotate() 操作会导致一个新元素成为开始迭代器所指向的第一个元素。在旋转之后,最后一个元素会在新的第一个元素之前。rotate() 的第一个
参数是这个序列的开始迭代器;第二个参数是指向新的第一个元素的迭代器,它必定在序列之内。第三个参数是这个序列的结束迭代器。这个算法会返回一个迭代器,它指向原始的第一个元素所在的新位置。例如:
std::vector<string> words { "one", "two", "three", "four", "five","six", "seven", "eight"};
auto iter = std::rotate(std::begin(words), std::begin(words)+3, std::end(words));
std::copy(std::begin(words), std::end(words),std::ostream_iterator<string> {std::cout, " "});
std::cout << std::endl << "First element before rotation: " << *iter << std::endl;
这段代码对 words 中的所有元素进行了旋转。执行这段代码会生成如下内容:
four five six seven eight one two three
First element before rotation: one
    输出说明 "four" 成为新的第一个元素,而且 rotate() 返回的迭代器指向之前的第一个元素"one"
    当然,不需要对容器中的全部元素进行旋转。例如:
std::vector<string> words { "one", "two", "three", "four", "five","six", "seven", "eight", "nine", "ten"};
auto start = std::find(std:rbegin(words), std::end(words), "two");
auto end_iter = std::find(std::begin(words), std::end(words), "eight");
auto iter = std::rotate(start, std::find(std::begin(words), std::end (words), "five") , end_iter);
std::copy(std::begin(words), std::end(words), std::ostream_iterator<string>{std::cout, " "});
std::cout << std::endl << "First element before rotation: " << *iter << std::endl;
这里用 find() 算法分别获取了和"two"、"eight"匹配的元素的迭代器。它们定义了被旋转的序列,这个序列是容器元素的子集。这个序列会被旋转为使"five"成为第一个元素,输出说明它是按预期工作的:
one five six seven two three four eight nine ten
First element before rotation: two
(http://c.biancheng.net/view/609.html)

[15]rotate_copy(STL rotate_copy)算法详解
    rotate_copy() 算法会在新序列中生成一个序列的旋转副本,并保持原序列不变。rotate_copy() 的前 3 个参数和 rotate() 是相同的;第 4 个参数是一个输出迭代器,它指向目的序列的第一个元素。
这个算法会返回一个目的序列的输出迭代器,它指向最后一个被复制元素的下一个位置。例如:
std::vector<string> words {"one", "two", "three", "four", "five","six", "seven", "eight", "nine","ten"};
auto start = std::find(std::begin(words), std::end(words), "two");
auto end_iter = std::find (std::begin(words) , std::end (words) ,"eight");
std::vector<string> words_copy;
std::rotate_copy(start, std::find(std::begin(words), std::end(words),"five") , end_iter, std::back_inserter (words_copy));
std::copy(std::begin(words_copy), std::end(words_copy),std::ostream_iterator<string> {std::cout, " "});
std::cout << std::endl;
    这段代码会对 word 中从 "two" 到 "seven" 的元素生成一个旋转副本。通过使用 back_insert_iterator 将复制的元素追加到 words_copy 容器中,back_insert_iterator 会调用 words_copy 
容器的成员函数 push_back() 来插入每个元素。这段代码产生的输出如下:
five six seven two three four

[16]fill和fill_n函数用法详解
    fill() 和 fill_n() 算法提供了一种为元素序列填入给定值的简单方式,fill() 会填充整个序列; fill_n() 则以给定的迭代器为起始位置,为指定个数的元素设置值。下面展示了 fill() 的用法:
std::vector<string> data {12}; // Container has 12 elements
std::fill (std::begin (data), std::end (data), "none"); // Set all elements to "none"
fill 的前两个参数是定义序列的正向迭代器,第三个参数是赋给每个元素的值。
   fill_n() 的参数分别是指向被修改序列的第一个元素的正向迭代器、被修改元素的个数以及要被设置的值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值