STL算法积累(二)

做~自己de王妃 提交于 2020-02-26 23:02:21

C++ sort 
stable_sort
partial_sort 
nth_element 
is_sorted 
is_sorted_until

[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”就是升序序列的最后一个元素

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!