max
和 min
max
和 min
是 C++ 标准库中的两个算法,用于返回两个元素中的最大值或最小值。它们有多个重载版本,可以通过默认比较操作或自定义比较函数来进行比较。
头文件:
#include <algorithm>
函数原型:
Type const &max(Type const &one, Type const &two);
Type const &max(Type const &one, Type const &two, Comparator comp);
Type const &min(Type const &one, Type const &two);
Type const &min(Type const &one, Type const &two, Comparator comp);
描述:
-
第一个原型:返回两个元素
one
和two
中的较大者。使用数据类型的operator>
来确定哪个元素更大。 -
第二个原型:返回
one
,如果comp(one, two)
返回true
,否则返回two
。这里comp
是一个二元谓词,用于决定两个元素的比较。 -
第三个原型:与第一个原型类似,但返回的是两个元素中的较小者,使用
operator<
来比较。 -
第四个原型:与第二个原型类似,但返回的是两个元素中的较小者,使用
comp
进行比较。
示例:
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
bool caseString(string const &first, string const &second)
{
return strcasecmp(first.c_str(), second.c_str()) <= 0;
}
string const &call(bool first, string const &lhs, string const &rhs)
{
return first ? max(lhs, rhs) : min(lhs, rhs);
}
string const &call(bool first, string const &lhs, string const &rhs, bool (*cmp)(string const &, string const &))
{
return first ? max(lhs, rhs, cmp) : min(lhs, rhs, cmp);
}
int main(int argc, char **argv) // no args: use max, else min
{
bool fun = argc == 1;
char const *where = fun ? "last\n" : "first\n";
cout << "Word '" << call(fun, "first", "second") <<
"' is lexicographically " << where <<
"Word '" << call(fun, "first", "SECOND") <<
"' is lexicographically " << where <<
"Word '" << call(fun, "first", "SECOND", caseString) <<
"' is lexicographically " << where;
}
输出结果:
- 无参数调用时:
Word 'second' is lexicographically last
Word 'first' is lexicographically last
Word 'SECOND' is lexicographically last
- 有参数调用时:
Word 'first' is lexicographically first
Word 'SECOND' is lexicographically first
Word 'first' is lexicographically first
总结:
max
和min
用于返回两个元素中的最大值或最小值。- 可以使用默认比较操作(
operator>
或operator<
)或自定义的比较函数来确定值的顺序。 - 示例中演示了如何使用
max
和min
以及自定义的比较函数来比较字符串。
max_element
/ min_element
/ minmax_element
max_element
和 min_element
用于找到给定范围内的最大元素和最小元素,而 minmax_element
同时找到最小和最大元素。
头文件:
#include <algorithm>
函数原型:
// 查找最大元素
ForwardIterator max_element([ExecPol,] ForwardIterator first, ForwardIterator last);
ForwardIterator max_element([ExecPol,] ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
// 查找最小元素
ForwardIterator min_element([ExecPol,] ForwardIterator first, ForwardIterator last);
ForwardIterator min_element([ExecPol,] ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
// 查找最小和最大元素
std::pair<ForwardIterator, ForwardIterator> minmax_element([ExecPol,] ForwardIterator first, ForwardIterator last);
std::pair<ForwardIterator, ForwardIterator> minmax_element([ExecPol,] ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
描述:
-
max_element
:- 第一个原型:返回指向最大元素的迭代器。使用
operator<
来比较元素。 - 第二个原型:使用自定义的二元谓词
pred
进行比较,返回满足pred
返回值为true
的元素。
- 第一个原型:返回指向最大元素的迭代器。使用
-
min_element
:- 第一个原型:返回指向最小元素的迭代器。使用
operator<
来比较元素。 - 第二个原型:使用自定义的二元谓词
pred
进行比较,返回满足pred
返回值为true
的元素。
- 第一个原型:返回指向最小元素的迭代器。使用
-
minmax_element
:- 第一个原型:返回一个
std::pair
,包含最小元素和最大元素的迭代器。使用operator<
来比较元素。 - 第二个原型:使用自定义的二元谓词
pred
进行比较,返回最小和最大元素的迭代器对。
- 第一个原型:返回一个
示例:
#include <algorithm>
#include <iostream>
#include <cstdlib>
using namespace std;
bool absCompare(int first, int second)
{
return abs(first) < abs(second);
}
int* maxMin(bool high, int* begin, int* end)
{
return high ? max_element(begin, end) : min_element(begin, end);
}
int* maxMin(bool high, int* begin, int* end, bool (*comp)(int, int))
{
return high ? max_element(begin, end, comp) : min_element(begin, end, comp);
}
int main(int argc, char **argv)
{
bool max = argc == 1;
char const* type = max ? "max" : "min";
int ia[] = {-4, 7, -2, 10, -12};
cout << "The " << type << " int value is " <<
*maxMin(max, ia, ia + 5) << "\n" <<
"The max. absolute int value is " <<
*maxMin(max, ia, ia + 5, absCompare) << '\n';
}
输出结果:
当没有命令行参数时:
The max int value is 10
The max. absolute int value is -12
当有命令行参数时:
The min int value is -12
The min. absolute int value is -2
总结:
max_element
和min_element
分别用于找到最大和最小元素,支持自定义比较函数。minmax_element
同时返回最小和最大元素,支持自定义比较函数。
merge
merge
函数用于将两个已排序的范围合并成一个新的已排序范围。
头文件:
#include <algorithm>
函数原型:
// 默认比较操作
OutputIterator merge([ExecPol,] InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result);
// 自定义比较操作
OutputIterator merge([ExecPol,] InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
描述:
-
第一个原型:将两个已排序的范围
[first1, last1)
和[first2, last2)
合并,保持排序顺序。使用数据类型的operator<
进行比较。结果存储在从result
开始的范围内,并且返回的迭代器指向合并后的范围的末尾。 -
第二个原型:将两个已排序的范围
[first1, last1)
和[first2, last2)
合并,保持排序顺序。使用自定义的比较函数comp
进行比较。结果存储在从result
开始的范围内,并且返回的迭代器指向合并后的范围的末尾。比较函数comp
需要接受两个元素作为参数,并返回布尔值。
示例:
#include <algorithm>
#include <string>
#include <cstring>
#include <iterator>
#include <iostream>
using namespace std;
// 自定义比较函数:不区分大小写的字符串比较
bool caseString(string const &first, string const &second)
{
return strcasecmp(first.c_str(), second.c_str()) < 0;
}
int main()
{
// 两个已排序的字符串数组
string range1[] = { "alpha", "bravo", "foxtrot", "hotel", "zulu" };
string range2[] = { "delta", "echo", "golf", "romeo" };
// 合并后的结果数组
string result[5 + 4];
// 使用默认比较操作合并
copy(result, merge(range1, range1 + 5, range2, range2 + 4, result),
ostream_iterator<string>{ cout, " " });
cout << '\n';
// 使用自定义比较函数进行合并(不区分大小写)
string range3[] = { "ALPHA", "bravo", "foxtrot", "HOTEL", "ZULU" };
string range4[] = { "delta", "ECHO", "GOLF", "romeo" };
copy(result, merge(range3, range3 + 5, range4, range4 + 4, result, caseString),
ostream_iterator<string>{ cout, " " });
cout << '\n';
}
输出结果:
alpha bravo delta echo foxtrot golf hotel romeo zulu
ALPHA bravo delta ECHO foxtrot GOLF HOTEL romeo ZULU
总结:
merge
函数可以将两个已排序的范围合并成一个新的已排序范围。- 支持默认比较操作和自定义比较操作。自定义比较操作可以实现更复杂的合并逻辑,比如不区分大小写的比较。
minmax
minmax
函数用于同时找出两个值中的最小值和最大值,并返回这两个值的配对。
头文件:
#include <algorithm>
#include <functional>
#include <iostream>
#include <initializer_list>
函数原型:
// 使用默认比较操作
pair<Type const &, Type const &> minmax(Type const &t1, Type const &t2);
// 使用自定义比较操作
pair<Type const &, Type const &> minmax(Type const &t1, Type const &t2, BinaryPred pred);
// 使用初始化列表,默认比较操作
pair<Type const &, Type const &> minmax(std::initializer_list<Type> values);
// 使用初始化列表,自定义比较操作
pair<Type const &, Type const &> minmax(std::initializer_list<Type> values, BinaryPred pred);
描述:
-
第一个原型:返回一个
std::pair
对象,其中first
成员是t1
和t2
中较小的值,second
成员是较大的值。比较使用Type
的operator<
。 -
第二个原型:功能与第一个原型相同,但使用自定义的比较函数
pred
进行比较。如果pred(t1, t2)
返回true
,则t1
被视为较小值。 -
第三个原型:功能与第一个原型类似,但适用于初始化列表中的值。返回的
std::pair
对象的first
成员是初始化列表中的最小值,second
成员是最大值。 -
第四个原型:功能与第二个原型类似,但适用于初始化列表中的值。使用自定义的比较函数
pred
进行比较。
示例:
#include <algorithm>
#include <functional>
#include <iostream>
#include <initializer_list>
#include <vector>
using namespace std;
int main() {
// 使用默认比较操作
auto values = minmax(5, 2);
cout << values.first << " is smaller than " << values.second << '\n';
// 使用自定义比较操作
auto values_custom = minmax(5, 2, less<int>{});
cout << values_custom.first << " is smaller than " << values_custom.second << '\n';
// 使用初始化列表
auto values_list = minmax({5, 2, 8, 1, 7});
cout << values_list.first << " is smaller than " << values_list.second << '\n';
// 使用初始化列表和自定义比较操作
auto values_list_custom = minmax({5, 2, 8, 1, 7}, greater<int>{});
cout << values_list_custom.first << " is larger than " << values_list_custom.second << '\n';
return 0;
}
输出:
2 is smaller than 5
2 is smaller than 5
1 is smaller than 8
8 is larger than 1
总结:
minmax
函数可以找出两个值中的最小值和最大值,并返回它们的配对。- 支持默认比较操作和自定义比较操作。
- 还可以处理初始化列表,支持对多个值进行最小值和最大值的比较。
mismatch
mismatch
函数用于比较两个序列中的元素,直到找到第一个不同的元素位置或到达第一个序列的结束。
头文件:
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
函数原型:
// 使用默认比较操作(operator==)
pair<InputIterator1, InputIterator2> mismatch(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2);
// 使用自定义比较操作
pair<InputIterator1, InputIterator2> mismatch(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, Compare comp);
描述:
-
第一个原型:比较从
first1
和first2
开始的两个序列的元素。使用数据类型的operator==
进行比较。比较在找到第一个不同的元素位置时停止,或者到达last1
。返回一个包含指向第一个不同元素的迭代器的std::pair
对象。第二个序列可以比第一个序列长。如果第二个序列比第一个序列短,行为未定义。 -
第二个原型:与第一个原型类似,但使用自定义的比较函数
comp
进行比较。比较在comp
返回false
时停止,或者到达last1
。返回一个包含指向第一个不同元素的迭代器的std::pair
对象。第二个序列可以比第一个序列长。如果第二个序列比第一个序列短,行为未定义。
示例:
#include <algorithm>
#include <cstring>
#include <iostream>
#include <string>
#include <utility>
using namespace std;
bool caseString(string const &first, string const &second) {
return strcasecmp(first.c_str(), second.c_str()) == 0;
}
int main() {
string range1[] = { "alpha", "bravo", "foxtrot", "hotel", "zulu" };
string range2[] = { "alpha", "bravo", "foxtrot", "Hotel", "zulu" };
// 使用默认比较操作
pair<string *, string *> pss = mismatch(range1, range1 + 5, range2);
cout << "The elements " << *pss.first << " and " << *pss.second <<
" differ at index " << (pss.first - range1) << '\n';
// 使用自定义比较操作
if (mismatch(range1, range1 + 5, range2, caseString).first == range1 + 5) {
cout << "When compared case-insensitively they match\n";
}
return 0;
}
输出:
The elements hotel and Hotel differ at index 3
When compared case-insensitively they match
总结:
mismatch
函数用于比较两个序列,直到找到第一个不同的元素位置。- 可以使用默认的
operator==
比较,也可以使用自定义的比较函数。 - 如果第二个序列比第一个序列短,行为未定义。
move
和 move_backward
这两个算法用于将元素从一个范围移动到另一个范围,其中 move
是前向移动,move_backward
是反向移动。
头文件:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
函数原型:
// 前向移动
OutputIter move([ExecPol,] InputIter first, InputIter last, OutputIter dest);
// 反向移动
BidirIter move_backward(BidirIter first, BidirIter last, BidirIter lastDest);
描述:
-
第一个原型 (
move
):将从迭代器范围[first, last)
中的所有元素移动到从dest
开始的范围中,并返回指向dest
范围最后一个元素之后位置的迭代器。移动操作会将元素从源范围转移到目标范围,但源范围中的元素在移动后仍然有效,但可能已被改变。 -
第二个原型 (
move_backward
):将从迭代器范围[first, last)
中的所有元素以反向顺序移动到从lastDest
开始的范围中,并返回指向lastDest
范围开始位置的迭代器。lastDest
不能指向[first, last)
范围中的元素。移动后,源范围中的元素仍然有效,但可能已被改变。
示例:
#include <algorithm>
#include <string>
#include <iostream>
#include <iterator>
using namespace std;
void show(string const *begin, string const *end) {
copy(begin, end, ostream_iterator<string>(cout, ", "));
cout << '\n';
}
int main() {
string sarr[] = { "alpha", "bravo", "charley", "delta" };
string dest[4] = { "" };
auto last = end(sarr);
auto lastDest = move(sarr, last, dest); // 将所有元素移动到 dest
cout << "sarr after move:\n";
show(sarr, last);
cout << "dest after move:\n";
show(dest, lastDest);
move_backward(dest, lastDest, last); // 将元素反向移动到 sarr
cout << "sarr after move_backward:\n";
show(sarr, last);
cout << "dest after move_backward:\n";
show(dest, lastDest);
return 0;
}
输出:
sarr after move:
, , , ,
dest after move:
alpha, bravo, charley, delta,
sarr after move_backward:
alpha, bravo, charley, delta,
dest after move_backward:
, , , ,
总结:
move
函数用于将元素从一个范围移动到另一个范围,并返回目标范围的结束迭代器。move_backward
函数用于将元素以反向顺序移动到另一个范围,并返回目标范围的开始迭代器。- 移动后的源范围中的元素仍然有效,但可能已被改变。
next_permutation
/ prev_permutation
这两个算法用于生成序列的下一个或上一个排列。
头文件:
#include <algorithm>
函数原型:
// 生成下一个排列
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last);
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last, Comp comp);
// 生成上一个排列
bool prev_permutation(BidirectionalIterator first, BidirectionalIterator last);
bool prev_permutation(BidirectionalIterator first, BidirectionalIterator last, Comp comp);
描述:
-
第一个原型 (
next_permutation
):生成给定范围[first, last)
中元素的下一个排列。对于序列 1, 2, 3,调用next_permutation
后,元素的排列顺序为:- 1 2 3
- 1 3 2
- 2 1 3
- 2 3 1
- 3 1 2
- 3 2 1
这个算法会将序列重新排序,生成下一个更大的排列。如果已经是最后一个排列,则返回false
,并将序列排序成第一个排列。如果重新排序成功,则返回true
。
-
第二个原型 (
next_permutation
与comp
):类似于第一个原型,但使用comp
作为比较函数来决定元素的排列顺序。comp
是一个二元谓词(返回true
或false
的函数)。 -
第三个和第四个原型 (
prev_permutation
):这些函数的行为与next_permutation
相同,但生成的是上一个排列而不是下一个排列。
示例:
#include <algorithm>
#include <iterator>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
bool caseString(string const &first, string const &second) {
return strcasecmp(first.c_str(), second.c_str()) < 0;
}
int main() {
string saints[] = { "Oh", "when", "the", "saints" };
cout << "All permutations of 'Oh when the saints':\nSequences:\n";
do {
copy(saints, saints + 4, ostream_iterator<string>{ cout, " " });
cout << '\n';
} while (next_permutation(saints, saints + 4, caseString));
cout << "After first sorting the sequence:\n";
sort(saints, saints + 4, caseString);
cout << "Sequences:\n";
do {
copy(saints, saints + 4, ostream_iterator<string>{ cout, " " });
cout << '\n';
} while (next_permutation(saints, saints + 4, caseString));
return 0;
}
输出(部分):
All permutations of 'Oh when the saints':
Sequences:
Oh when the saints
Oh when saints the
Oh saints the when
Oh saints when the
Oh the saints when
Oh the when saints
...
After first sorting the sequence:
Sequences:
Oh saints the when
Oh saints when the
Oh the saints when
Oh the when saints
...
总结:
next_permutation
用于生成当前序列的下一个排列,并返回true
或false
,表示是否成功生成了下一个排列。prev_permutation
用于生成当前序列的上一个排列,也返回true
或false
。- 两者均可以使用自定义的比较函数
comp
来确定元素的排列顺序。
nth_element
头文件:
#include <algorithm>
函数原型:
// 只使用默认比较函数
void nth_element([ExecPol,] RandomAccessIterator first,
RandomAccessIterator nth, RandomAccessIterator last);
// 使用自定义比较函数
void nth_element([ExecPol,] RandomAccessIterator first,
RandomAccessIterator nth, RandomAccessIterator last,
Compare comp);
描述:
-
第一个原型:在范围
[first, last)
中,将元素相对于nth
指向的元素进行排序,使得[first, nth)
中的所有元素都小于nth
指向的元素,而[nth + 1, last)
中的所有元素都大于nth
指向的元素。nth
指向的元素最终处于排序后的中间位置,但[first, nth)
和[nth + 1, last)
中的元素不会被进一步排序。比较使用operator<
。 -
第二个原型:与第一个原型类似,但使用
comp
作为比较函数来进行元素比较。comp
是一个二元谓词(返回true
或false
的函数)。
示例:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <functional>
using namespace std;
int main() {
int ia[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
nth_element(ia, ia + 3, ia + 10);
cout << "sorting with respect to " << ia[3] << '\n';
copy(ia, ia + 10, ostream_iterator<int>(cout, " "));
cout << '\n';
nth_element(ia, ia + 5, ia + 10, greater<int>());
cout << "sorting with respect to " << ia[5] << '\n';
copy(ia, ia + 10, ostream_iterator<int>(cout, " "));
cout << '\n';
return 0;
}
输出:
sorting with respect to 4
1 2 3 4 9 7 5 6 8 10
sorting with respect to 5
10 8 7 9 6 5 3 4 2 1
总结:
nth_element
函数用于对范围[first, last)
中的元素进行部分排序,使得nth
指向的元素在最终排列中处于正确的位置,所有小于nth
指向的元素都在其前面,所有大于nth
指向的元素都在其后面。- 使用默认比较函数时,元素相对于
nth
指向的元素是以operator<
为基准的;使用自定义比较函数时,则按照comp
函数的定义进行排序。
partial_sort
和 partial_sort_copy
头文件:
#include <algorithm>
函数原型:
-
partial_sort
:void partial_sort([ExecPol,] RandomAccessIterator begin, RandomAccessIterator middle, RandomAccessIterator end); void partial_sort([ExecPol,] RandomAccessIterator begin, RandomAccessIterator middle, RandomAccessIterator end, BinaryPredicate pred);
-
partial_sort_copy
:void partial_sort_copy([ExecPol,] InputIterator begin, InputIterator end, RandomAccessIterator dest_begin, RandomAccessIterator dest_end); void partial_sort_copy([ExecPol,] InputIterator begin, InputIterator end, RandomAccessIterator dest_begin, RandomAccessIterator dest_end, BinaryPredicate pred);
描述:
-
partial_sort
:- 第一个原型:将范围
[begin, end)
中的前(middle - begin)
个最小元素进行排序,并存储在[begin, middle)
中。其余的元素[middle, end)
保持不变。 - 第二个原型:将范围
[begin, end)
中的前(middle - begin)
个最小元素(根据自定义的比较函数pred
)进行排序,并存储在[begin, middle)
中,其余的元素保持不变。
- 第一个原型:将范围
-
partial_sort_copy
:- 第一个原型:将范围
[begin, end)
中的前(dest_end - dest_begin)
个最小元素复制到目标范围[dest_begin, dest_end)
中。源范围和目标范围的元素个数取较小值。 - 第二个原型:将范围
[begin, end)
中的前(dest_end - dest_begin)
个最小元素(根据自定义的比较函数pred
)复制到目标范围[dest_begin, dest_end)
中。源范围和目标范围的元素个数取较小值。
- 第一个原型:将范围
示例:
#include <algorithm>
#include <iostream>
#include <functional>
#include <iterator>
using namespace std;
int main() {
int ia[] = {1, 9, 5, 3, 7, 2, 4, 6, 8, 10};
int ia2[6];
// 复制范围 [ia, ia + 10) 中的前 6 个最小元素到 ia2
partial_sort_copy(ia, ia + 10, ia2, ia2 + 6);
cout << "the 6 smallest elements: ";
copy(ia2, ia2 + 6, ostream_iterator<int>(cout, " "));
cout << '\n';
// 在范围 [ia, ia + 10) 中,将前 3 个最小元素排序到 [ia, ia + 3)
partial_sort(ia, ia + 3, ia + 10);
cout << "find the 3 smallest elements:\n";
copy(ia, ia + 3, ostream_iterator<int>(cout, " "));
cout << '\n';
// 在范围 [ia, ia + 10) 中,将前 5 个最大元素排序到 [ia, ia + 5)
partial_sort(ia, ia + 5, ia + 10, greater<int>());
cout << "find the 5 largest elements:\n";
copy(ia, ia + 5, ostream_iterator<int>(cout, " "));
cout << '\n';
return 0;
}
输出:
the 6 smallest elements: 1 2 3 4 5 6
find the 3 smallest elements:
1 2 3
find the 5 largest elements:
10 9 8 7 6
总结:
partial_sort
用于部分排序源范围中的元素,只对前面一部分进行排序,其余元素保持原状。partial_sort_copy
用于将源范围中前面一部分元素(按照指定的排序规则)复制到目标范围,目标范围的大小取决于源范围和目标范围的较小值。
partial_sum
头文件:
#include <numeric>
#include <algorithm>
#include <iostream>
#include <functional>
#include <iterator>
函数原型:
partial_sum
:OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result); OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation op);
描述:
-
第一个原型:计算范围
[first, last)
的部分和,并将结果存储在[result, <返回的 OutputIterator>)
中。结果范围的第一个元素等于first
指向的元素。后续元素是前面元素的累计和。 -
第二个原型:计算范围
[first, last)
的部分累积,使用二元操作符op
计算每个元素的值。结果范围的第一个元素等于first
指向的元素。后续元素是前一个结果元素和当前元素的操作结果。
示例:
#include <numeric>
#include <algorithm>
#include <iostream>
#include <functional>
#include <iterator>
using namespace std;
int main() {
int ia[] = {1, 2, 3, 4, 5};
int ia2[5];
// 计算部分和
copy(ia2, partial_sum(ia, ia + 5, ia2), ostream_iterator<int>(cout, " "));
cout << '\n';
// 使用乘法计算部分积
copy(ia2, partial_sum(ia, ia + 5, ia2, multiplies<int>()), ostream_iterator<int>(cout, " "));
cout << '\n';
return 0;
}
输出:
1 3 6 10 15
1 2 6 24 120
总结:
-
partial_sum
的第一个原型用于计算指定范围内的部分和,结果从first
开始,后续的每个元素是之前所有元素的累计和。 -
partial_sum
的第二个原型允许使用自定义的二元操作符op
来计算部分累积,例如可以用来计算部分积等。
partition
/ partition_point
/ stable_partition
头文件:
#include <algorithm>
函数原型:
-
partition
:BidirectionalIterator partition([ExecPol,] BidirectionalIterator first, BidirectionalIterator last, UnaryPredicate pred);
-
stable_partition
:BidirectionalIterator stable_partition([ExecPol,] BidirectionalIterator first, BidirectionalIterator last, UnaryPredicate pred);
-
partition_point
:ForwardIterator partition_point(ForwardIterator first, ForwardIterator last, UnaryPredicate pred);
描述:
-
partition
:将范围[first, last)
中所有使得一元谓词pred
返回true
的元素移到范围的前面,其余元素放在后面。这个操作并不会保持元素的原始顺序。 -
stable_partition
:与partition
类似,但保持谓词pred
返回true
和false
的元素各自的相对顺序。 -
partition_point
:返回一个迭代器,指向第一个使得谓词pred
返回false
的元素的位置。如果所有元素都使谓词pred
返回true
,则返回last
。
示例:
#include <algorithm>
#include <iostream>
#include <string>
#include <iterator>
using namespace std;
void show(int *begin, int *end) {
copy(begin, end, ostream_iterator<int>{ cout, " " });
cout << '\n';
}
int main() {
int org[] = {1, 3, 5, 7, 9, 10, 2, 8, 6, 4};
int ia[10];
copy(org, org + 10, ia);
auto lessThan4 = [](int value) {
return value <= 4;
};
// 使用 partition
int *split = partition(ia, ia + 10, lessThan4);
cout << "Last ia[]- element <= 4 is ia[" << split - ia - 1 << "]\n";
show(ia, end(ia));
// 使用 stable_partition
copy(org, org + 10, ia);
split = stable_partition(ia, ia + 10, lessThan4);
cout << "Last org[]-element <= 4 is ia[" << split - ia - 1 << "]\n";
show(ia, end(ia));
// 使用 partition_point
cout << "org[]-elements up to the partition point 4 are:\n";
show(org, partition_point(org, org + 10, lessThan4));
return 0;
}
输出:
Last ia[]- element <= 4 is ia[3]
1 3 4 2 9 10 7 8 6 5
Last org[]-element <= 4 is ia[3]
1 3 2 4 5 7 9 10 8 6
org[]-elements up to the partition point 4 are:
1 3
总结:
-
partition
:重新排列元素,使得所有满足谓词的元素在前,但不保持原始顺序。 -
stable_partition
:类似于partition
,但保持元素的相对顺序。 -
partition_point
:返回第一个不满足谓词的元素的迭代器。
partition_copy
头文件:
#include <algorithm>
#include <string>
#include <iostream>
#include <iterator>
函数原型:
std::pair<ForwardIter2, ForwardIter3> partition_copy([ExecPol,]
ForwardIter1 first, ForwardIter1 last,
ForwardIter2 trueDest,
ForwardIter3 falseDest,
UnaryPredicate pred);
描述:
- 所有在范围
[first, last)
内使得谓词pred
返回true
的元素被复制到以trueDest
开始的范围中,而其余元素被复制到以falseDest
开始的范围中。范围[first, last)
与trueDest
和falseDest
的范围不能重叠。
示例:
#include <algorithm>
#include <string>
#include <iostream>
#include <iterator>
using namespace std;
// 定义谓词函数
bool pred(const std::string &str) {
return "aeiou"s.find(str.front()) != string::npos;
}
// 显示函数
void show(const string *begin, const string *end) {
copy(begin, end, ostream_iterator<string>(cout, ", "));
cout << '\n';
}
int main() {
// 初始化字符串数组
string sarr[] = {
"alpha", "bravo", "charley", "delta", "echo",
"foxtrot", "golf", "hotel"
};
// 创建存储分组结果的数组
string trueDest[size(sarr)];
string falseDest[size(sarr)];
// 执行 partition_copy
pair<string*, string*> lastTF =
partition_copy(sarr, end(sarr), trueDest, falseDest, pred);
// 显示结果
cout << "pred() == true elements:\n";
show(trueDest, lastTF.first);
cout << "pred() == false elements:\n";
show(falseDest, lastTF.second);
return 0;
}
输出:
pred() == true elements:
alpha, echo,
pred() == false elements:
bravo, charley, delta, foxtrot, golf, hotel,
总结:
partition_copy
:将范围[first, last)
内的元素根据谓词pred
的返回值分别复制到两个目标范围中,一个是满足谓词的元素,另一个是不满足谓词的元素。
reduce
算法
头文件
#include <numeric>
函数原型
Type reduce([ExecPol,] InputIterator first, InputIterator last);
Type reduce([ExecPol,] InputIterator first, InputIterator last, Type init);
Type reduce([ExecPol,] InputIterator first, InputIterator last, BinaryOperation op);
Type reduce([ExecPol,] InputIterator first, InputIterator last, Type init, BinaryOperation op);
描述
reduce
算法类似于 accumulate
,但要求所用的操作符必须是结合律和交换律的:对元素的重组和排列顺序不会影响最终结果。例如,数值加法操作符满足这两个要求。
- 第一个原型:对区间
[first, last)
中的元素应用operator+
,返回结果的和。 - 第二个原型:对初始值
init
(左侧参数) 和区间[first, last)
中的元素应用二元操作符op
。返回计算结果的和。 - 最后两个原型:与前两个原型类似,但使用二元操作符
op
替代operator+
。计算时,先将最终返回的变量作为操作符的左侧参数,然后用迭代器区间中的元素作为右侧参数,操作符的返回值将赋值给最终返回的变量。
示例代码
// 编译命令: g++ -O2 reduce.cc -ltbb
#include <numeric>
#include <vector>
#include <execution>
#include <iostream>
using namespace std;
int main()
{
int ia[] = {1, 2, 3, 4};
vector<int> iv(ia, ia + 4);
// 用于演示:对4个值使用并行执行
cout <<
"Sum: " << reduce(execution::par, iv.begin(), iv.end(), int()) << "\n"
"Product: " << reduce(iv.begin(), iv.end(), int(1), multiplies<int>{}) << '\n';
}
输出
Sum: 10
Product: 24
remove
, remove_if
, remove_copy
, remove_copy_if
算法
头文件
#include <algorithm>
函数原型
ForwardIterator remove([ExecPol,] ForwardIterator first, ForwardIterator last, Type const &value);
OutputIterator remove_copy([ExecPol,] InputIterator first, InputIterator last, OutputIterator dest, Type const &value);
OutputIterator remove_copy_if([ExecPol,] InputIterator first, InputIterator last, OutputIterator dest, UnaryPredicate pred);
ForwardIterator remove_if([ExecPol,] ForwardIterator first, ForwardIterator last, UnaryPredicate pred);
描述
-
第一个原型:将
[first, last)
区间内的元素重新排序,使所有不等于value
的值位于区间的开头。返回的前向迭代器指向重新排序后可以被移除的第一个元素。范围[returnvalue, last)
称为算法的剩余部分。注意,剩余部分可能包含不同于value
的元素,但这些元素可以安全地移除,因为这些元素也出现在[first, returnvalue)
区间中。这种重复是因为算法是复制而不是移动元素到新位置。函数使用数据类型的operator==
来确定要移除的元素。 -
第二个原型:将
[first, last)
区间中不匹配value
的元素复制到[dest, returnvalue)
区间,其中returnvalue
是函数返回的值。原区间[first, last)
不会被修改。函数使用数据类型的operator==
来确定哪些元素不需要复制。 -
第三个原型:对于
[first, last)
区间内的元素,如果一元谓词pred
返回true
,这些元素不会插入到结果迭代器中。所有其他元素被复制到[dest, returnvalue)
区间,其中returnvalue
是函数返回的值。原区间[first, last)
不会被修改。 -
第四个原型:将
[first, last)
区间内的元素重新排序,使所有一元谓词pred
返回false
的值位于区间的开头,并保持其相对顺序。返回的前向迭代器指向重新排序后一元谓词pred
返回true
的第一个元素。范围[returnvalue, last)
称为算法的剩余部分。剩余部分可能包含一元谓词pred
返回false
的元素,但这些元素可以安全地移除,因为这些元素也出现在[first, returnvalue)
区间中。这种重复是因为算法是复制而不是移动元素到新位置。
注意,复制重载函数期望输出迭代器。如果要保留的元素存储在例如 vector
中,则可以将 kept.begin()
作为函数的 dest
参数。然而,这要求在调用函数之前 size(kept)
必须足够大以容纳所有保留的元素。另一种方法是使用 back_inserter
确保 kept
在函数返回后仅包含保留的元素。这种方法在示例程序中使用。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <iterator>
#include <vector>
using namespace std;
using StrVect = vector<string>;
bool judge(string const &word)
{
return count(word.begin(), word.end(), 'a') > 1;
}
void show(auto const &begin, auto const &end)
{
copy(begin, end, ostream_iterator<string>(cout, ", "));
cout << "\n";
}
int main()
{
StrVect words =
{
"kilo", "alpha", "lima", "mike", "alpha", "november",
"alpha", "alpha", "alpha", "papa", "quebec"
};
auto src{ words };
cout << "Removing all \"alpha\"s:\n";
auto end = remove(src.begin(), src.end(), "alpha");
show(src.begin(), end);
cout << "Leftover elements are:\n";
show(end, src.end());
src = words;
cout << "Remove_copy_if removes words having > 1 'a' chars:\n";
StrVect kept;
remove_copy_if(src.begin(), src.end(), back_inserter(kept), judge);
show(kept.begin(), kept.end());
}
输出
Removing all "alpha"s:
kilo, lima, mike, november, papa, quebec,
Leftover elements are:
alpha, alpha, alpha,
Remove_copy_if removes words having > 1 'a' chars:
kilo, lima, mike, november, quebec,
replace
, replace_if
, replace_copy
, replace_copy_if
算法
头文件
#include <algorithm>
函数原型
void replace([ExecPol,] ForwardIterator first, ForwardIterator last, Type const &oldvalue, Type const &newvalue);
ForwardIterator replace_if([ExecPol,] ForwardIterator first, ForwardIterator last, UnaryPredicate pred, Type const &value);
OutputIterator replace_copy([ExecPol,] InputIterator first, InputIterator last, OutputIterator result, Type const &oldvalue, Type const &newvalue);
OutputIterator replace_copy_if([ExecPol,] ForwardIterator first, ForwardIterator last, OutputIterator result, UnaryPredicate pred, Type const &value);
描述
-
第一个原型:将
[first, last)
区间内所有等于oldvalue
的元素替换为newvalue
的副本。算法使用数据类型的operator==
来比较元素。 -
第二个原型:将
[first, last)
区间内一元谓词pred
返回true
的元素替换为value
。 -
第三个原型:将
[first, last)
区间内所有等于oldvalue
的元素替换为newvalue
,并将结果发送到输出迭代器result
中。算法使用数据类型的operator==
来比较元素。 -
第四个原型:将
[first, last)
区间内一元谓词pred
返回false
的元素发送到结果输出迭代器中,如果pred
返回true
,则将value
发送到结果输出迭代器中。原区间[first, last)
不会被修改。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
using namespace std;
using StrVect = vector<string>;
void show(StrVect const &vect)
{
copy(vect.begin(), vect.end(), ostream_iterator<string>(cout, " "));
cout.put('\n');
}
bool isAlpha(string const &str)
{
return str == "alpha";
}
int main()
{
StrVect words =
{
"kilo", "alpha", "lima", "mike", "alpha", "november",
"alpha", "oscar", "alpha", "alpha", "papa"
};
// 使用 replace 替换 "alpha" 为 "ALPHA"
// replace(words.begin(), words.end(), "alpha"s, "ALPHA"s);
// show(words);
// 使用 replace_if 替换符合谓词的元素
// replace_if(words.begin(), words.end(), isAlpha, "ALPHA"s);
// show(words);
// 使用 replace_copy 生成新的结果容器
// StrVect result;
// replace_copy(words.begin(), words.end(), back_inserter(result), "alpha"s, "ALPHA"s);
// show(result);
// 使用 replace_copy_if 生成新的结果容器
StrVect result;
replace_copy_if(words.begin(), words.end(), back_inserter(result), isAlpha, "ALPHA"s);
show(result);
}
输出
kilo ALPHA lima mike ALPHA november ALPHA oscar ALPHA ALPHA papa
reverse
, reverse_copy
算法
头文件
#include <algorithm>
函数原型
void reverse([ExecPol,] BidirectionalIterator first, BidirectionalIterator last);
OutputIterator reverse_copy([ExecPol,] BidirectionalIterator first, BidirectionalIterator last, OutputIterator dest);
描述
-
第一个原型:将
[first, last)
区间内的元素顺序反转。 -
第二个原型:将
[first, last)
区间内的元素以反转顺序插入到dest
中,返回最后一个插入元素后的输出迭代器。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
using namespace std;
using StrVect = vector<string>;
void show(StrVect const &vect)
{
copy(vect.begin(), vect.end(), ostream_iterator<string>(cout, " "));
cout.put('\n');
}
int main(int argc, char **argv)
{
StrVect words =
{
"alpha", "kilo", "lima", "mike",
"november", "oscar", "papa"
};
if (argc == 1) {
// 没有参数:直接反转
reverse(words.begin(), words.end());
show(words);
return 0;
}
StrVect dest;
reverse_copy(words.begin(), words.end(), back_inserter(dest));
show(dest);
}
输出
papa oscar november mike lima kilo alpha
rotate
, rotate_copy
算法
头文件
#include <algorithm>
函数原型
void rotate([ExecPol,] ForwardIterator first, ForwardIterator middle, ForwardIterator last);
OutputIterator rotate_copy([ExecPol,] ForwardIterator first, ForwardIterator middle, ForwardIterator last, OutputIterator result);
描述
-
第一个原型:将区间
[first, middle)
的元素移动到容器的末尾,将区间[middle, last)
的元素移动到容器的开头,同时保持这两个子区间内元素的顺序不变。 -
第二个原型:将区间
[middle, last)
的元素,然后是区间[first, middle)
的元素,按照顺序插入到result
中,返回最后一个插入元素后的输出迭代器。源区间的元素顺序不会被改变。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <iterator>
#include <vector>
using namespace std;
using StrVect = vector<string>;
void show(StrVect const &vect)
{
copy(vect.begin(), vect.end(), ostream_iterator<string>(cout, " "));
cout.put('\n');
}
int main(int argc, char **argv)
{
StrVect words =
{
"kilo", "lima", "mike", "november", "oscar",
"foxtrot", "golf", "hotel", "india", "juliet"
};
if (argc == 1) {
// 没有参数:直接旋转
rotate(words.begin(), words.begin() + words.size() / 2, words.end());
show(words);
return 0;
}
StrVect dest;
rotate_copy(words.begin(), words.begin() + words.size() / 2, words.end(), back_inserter(dest));
show(dest);
}
输出
foxtrot golf hotel india juliet kilo lima mike november oscar
sample
算法
头文件
#include <algorithm>
#include <random>
函数原型
OutputIterator sample(InputIterator first, InputIterator last, OutputIterator out, size_t sampleSize, Generator &&generator);
描述
- 从输入区间
[first, last)
中随机选择sampleSize
个元素(不重复),并将它们复制到目标区间,目标区间从out
开始,到函数的返回值结束。如果sampleSize
大于或等于last - first
,则选择所有元素。generator
是一个(均匀的)随机数生成器,比如mt19937
。
示例代码
#include <algorithm>
#include <iostream>
#include <random>
#include <string>
using namespace std;
int main()
{
string src{ "abcdefghijklmnopqrstuvwxyz" };
string dest;
// 从 src 中随机选择 7 个字母
sample(src.begin(), src.end(), back_inserter(dest), 7, mt19937{});
cout << "从 " << src << " 中随机选择的七个字母是: " << dest << '\n';
}
输出示例
从 abcdefghijklmnopqrstuvwxyz 中随机选择的七个字母是: bciorux
search
, search_n
算法
头文件
#include <algorithm>
函数原型
ForwardIterator search([ExecPol,] ForwardIterator first1, ForwardIterator last1, ForwardIterator first2, ForwardIterator last2);
ForwardIterator search([ExecPol,] ForwardIterator first1, ForwardIterator last1, ForwardIterator first2, ForwardIterator last2, BinaryPredicate pred);
constexpr ForwardIterator search([ExecPol,] ForwardIterator first, ForwardIterator last, Searcher const &searcher);
ForwardIterator search_n([ExecPol,] ForwardIterator first, ForwardIterator last, Size count, Type const &value);
ForwardIterator search_n([ExecPol,] ForwardIterator first1, ForwardIterator last1, Size count, Type const &value, BinaryPredicate pred);
描述
-
第一个原型:返回一个迭代器指向
first1
到last1
范围中第一个找到的[first2, last2)
区间的开始位置。如果没有找到,则返回last1
。 -
第二个原型:与第一个原型类似,但使用提供的二元谓词
pred
来比较两个区间的元素。如果没有找到,则返回last1
。 -
第三个原型:使用
searcher
对first
到last
范围执行搜索,searcher
接收这个范围,并返回找到的迭代器(如果没有找到,则返回last
)。 -
第四个和第五个原型:检查
[first, last)
范围内是否存在一个长度为count
的序列,其中每个元素都等于value
。返回第一个符合条件的迭代器。如果没有找到,则返回last
。第五个原型使用谓词pred
来判断元素是否等于value
。
示例代码
#include <algorithm>
#include <iostream>
#include <iterator>
using namespace std;
bool absEq(int i1, int i2)
{
return abs(i1) == abs(i2);
}
int main()
{
int range1[] = {-2, -4, -6, -8, 2, 4, 4, 6, 8};
int range2[] = {6, 8};
copy(
search(range1, end(range1), range2, range2 + 2),
end(range1),
ostream_iterator<int>(cout, " ")
);
cout << '\n';
copy(
search(range1, end(range1), range2, range2 + 2, absEq),
end(range1),
ostream_iterator<int>(cout, " ")
);
cout << '\n';
copy(
search_n(range1, end(range1), 2, 4, absEq),
end(range1),
ostream_iterator<int>(cout, " ")
);
cout << '\n';
}
输出
6 8
-6 -8 2 4 4 6 8
4 4 6 8
set_difference
算法
头文件
#include <algorithm>
函数原型
OutputIterator set_difference([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
OutputIterator set_difference([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
描述
-
第一个原型:从
[first1, last1)
范围中选择那些不在[first2, last2)
范围中的元素,并将它们按排序顺序放置在result
开始的位置。两个范围中的元素必须已经按operator<
排序。 -
第二个原型:与第一个原型类似,但使用比较函数对象
comp
来比较两个范围中的元素。两个范围中的元素必须按照comp
排序。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <iterator>
using namespace std;
bool caseless(string const &left, string const &right)
{
return strcasecmp(left.c_str(), right.c_str()) < 0;
}
int main()
{
string set1[] = { "kilo", "lima", "mike", "november", "oscar", "papa", "quebec" };
string set2[] = { "papa", "quebec", "romeo" };
string result[7];
// 查找 set1 中不在 set2 中的元素
copy(
result,
set_difference(set1, set1 + 7, set2, set2 + 3, result),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
string set3[] = { "PAPA", "QUEBEC", "ROMEO" };
// 查找 set1 中不在 set3 中的元素,忽略大小写
copy(
result,
set_difference(set1, set1 + 7, set3, set3 + 3, result, caseless),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
}
输出
kilo lima mike november oscar
kilo lima mike november oscar
set_intersection
算法
头文件
#include <algorithm>
函数原型
OutputIterator set_intersection([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
OutputIterator set_intersection([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
描述
-
第一个原型:从
[first1, last1)
范围中选择那些也存在于[first2, last2)
范围中的元素,并将它们按排序顺序放置在result
开始的位置。两个范围中的元素必须已经按照operator<
排序。 -
第二个原型:与第一个原型类似,但使用比较函数对象
comp
来比较两个范围中的元素。两个范围中的元素必须按照comp
排序。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <iterator>
using namespace std;
bool caseless(string const &left, string const &right)
{
return strcasecmp(left.c_str(), right.c_str()) < 0;
}
int main()
{
string set1[] = { "kilo", "lima", "mike", "november", "oscar", "papa", "quebec" };
string set2[] = { "papa", "quebec", "romeo" };
string result[7];
// 查找 set1 和 set2 中的交集
copy(
result,
set_intersection(set1, set1 + 7, set2, set2 + 3, result),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
string set3[] = { "PAPA", "QUEBEC", "ROMEO" };
// 查找 set1 和 set3 中的交集,忽略大小写
copy(
result,
set_intersection(set1, set1 + 7, set3, set3 + 3, result, caseless),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
}
输出
papa quebec
papa quebec
set_symmetric_difference
算法
头文件
#include <algorithm>
函数原型
OutputIterator set_symmetric_difference([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
OutputIterator set_symmetric_difference([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
描述
-
第一个原型:返回
[first1, last1)
范围内存在但不在[first2, last2)
范围内的元素,以及[first2, last2)
范围内存在但不在[first1, last1)
范围内的元素,按排序顺序存放在result
开始的位置。两个范围中的元素必须已经按照operator<
排序。 -
第二个原型:与第一个原型类似,但使用比较函数对象
comp
来比较两个范围中的元素。两个范围中的元素必须按照comp
排序。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <iterator>
using namespace std;
bool caseless(string const &left, string const &right)
{
return strcasecmp(left.c_str(), right.c_str()) < 0;
}
int main()
{
string set1[] = { "kilo", "lima", "mike", "november", "oscar", "papa", "quebec" };
string set2[] = { "papa", "quebec", "romeo" };
string result[7];
// 查找 set1 和 set2 中的对称差集
copy(
result,
set_symmetric_difference(set1, set1 + 7, set2, set2 + 3, result),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
string set3[] = { "PAPA", "QUEBEC", "ROMEO" };
// 查找 set1 和 set3 中的对称差集,忽略大小写
copy(
result,
set_symmetric_difference(set1, set1 + 7, set3, set3 + 3, result, caseless),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
}
输出
kilo lima mike november oscar romeo
kilo lima mike november oscar ROMEO
set_union
算法
头文件
#include <algorithm>
函数原型
OutputIterator set_union([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
OutputIterator set_union([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
描述
-
第一个原型:返回
[first1, last1)
范围内与[first2, last2)
范围内的元素的并集,按排序顺序存放在result
开始的位置。两个范围中的元素必须已经按照operator<
排序。 -
第二个原型:与第一个原型类似,但使用比较函数对象
comp
来比较两个范围中的元素。两个范围中的元素必须按照comp
排序。最终的结果中每个元素只出现一次,除非源范围内有重复元素,则结果中也会保留这些重复元素。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <iterator>
#include <vector>
using namespace std;
bool caseless(string const &left, string const &right)
{
return strcasecmp(left.c_str(), right.c_str()) < 0;
}
int main()
{
string set1[] = { "kilo", "lima", "mike", "november", "oscar", "papa", "quebec" };
string set2[] = { "papa", "quebec", "romeo" };
string result[8];
// 查找 set1 和 set2 中的并集
copy(
result,
set_union(set1, set1 + 7, set2, set2 + 3, result),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
string set3[] = { "PAPA", "QUEBEC", "ROMEO" };
// 查找 set1 和 set3 中的并集,忽略大小写
copy(
result,
set_union(set1, set1 + 7, set3, set3 + 3, result, caseless),
ostream_iterator<string>(cout, " ")
);
cout << '\n';
std::vector<int> v1 = {1, 2, 3, 4, 5, 5, 5};
std::vector<int> v2 = {3, 3, 3, 4, 5, 6, 7};
// 查找 v1 和 v2 中的并集
set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), ostream_iterator<int>(cout, " "));
cout << '\n';
}
输出
kilo lima mike november oscar papa quebec romeo
kilo lima mike november oscar papa quebec ROMEO
1 2 3 3 3 4 5 5 5 6 7
sort
和 stable_sort
算法
头文件
#include <algorithm>
函数原型
void sort([ExecPol,] RandomAccessIterator first, RandomAccessIterator last);
void sort([ExecPol,] RandomAccessIterator first, RandomAccessIterator last, Compare comp);
void stable_sort([ExecPol,] RandomAccessIterator first, RandomAccessIterator last);
void stable_sort([ExecPol,] RandomAccessIterator first, RandomAccessIterator last, Compare comp);
描述
-
第一个原型:对
[first, last)
范围内的元素进行升序排序,使用operator<
进行比较。 -
第二个原型:对
[first, last)
范围内的元素进行升序排序,使用comp
函数对象进行比较。comp
应返回true
,如果第一个参数在排序序列中应排在第二个参数之前。 -
第三个和第四个原型:与前两个原型相同,但使用稳定排序,保持相等元素的初始顺序不变。
示例代码
// 编译方式: g++ -O2 sort.cc -ltbb
#include <algorithm>
#include <cstdlib>
#include <execution>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
using namespace std;
int main() {
string words[] = {"november", "kilo", "mike", "lima", "oscar", "quebec", "papa"};
// 使用默认的升序排序
sort(words, words + 7);
copy(words, words + 7, ostream_iterator<string>(cout, " "));
cout << '\n';
// 使用降序排序
sort(words, words + 7, greater<string>());
copy(words, words + 7, ostream_iterator<string>(cout, " "));
cout << '\n';
// 随机生成 10,000 个整数并排序
int *vect = new int[10'000];
generate(execution::par, vect, vect + 10'000, rand);
cout << "sorted: " << is_sorted(vect, vect + 10'000) << '\n';
sort(execution::par, vect, vect + 10'000);
cout << "sorted: " << is_sorted(vect, vect + 10'000) << '\n';
delete[] vect;
// 稳定排序
using Pair = pair<size_t, string>; // 天数与月份
vector<Pair> months = {
{31, "Jan."}, {28, "Feb."}, {31, "Mar."}, {30, "Apr."},
{31, "May."}, {30, "Jun."}, {31, "Jul."}, {31, "Aug."},
{30, "Sep."}, {31, "Oct."}, {30, "Nov."}, {31, "Dec."}
};
stable_sort(months.begin(), months.end(),
[&](Pair const &lhs, Pair const &rhs) {
return lhs.first > rhs.first;
});
for (size_t idx = 0; auto const &month : months) {
cout << month.first << ": " << month.second
<< (++idx % 4 == 0 ? "\n" : " ");
}
}
输出
kilo lima mike november oscar papa quebec
quebec papa oscar november mike lima kilo
sorted: 0 // unordered sequence
sorted: 1 // sorted sequence
31: Jan. 31: Mar. 31: May. 31: Jul.
31: Aug. 31: Oct. 31: Dec. 30: Apr.
30: Jun. 30: Sep. 30: Nov. 28: Feb.
在示例中,还展示了如何使用 execution::par
执行策略来并行生成和排序整数数组。
shift_left
和 shift_right
算法
头文件
#include <algorithm>
函数原型
ForwardIterator shift_left(ForwardIterator first, ForwardIterator last, ForwardIterator new_first);
ForwardIterator shift_right(ForwardIterator first, ForwardIterator last, ForwardIterator new_last);
描述
-
shift_left
:将[first, last)
范围内的元素向左移动,使得[first, new_first)
范围内的元素被移动到[new_first, last)
范围的末尾。移动后,new_first
和[first, new_first)
之间的元素会被填补new_first
后面的空位。返回new_first
的位置。 -
shift_right
:将[first, last)
范围内的元素向右移动,使得[last - (new_last - first), last)
范围内的元素被移动到[first, new_last)
范围的前面。移动后,new_last
和[last - (new_last - first), last)
之间的元素会被填补到new_last
前面的空位。返回new_last
的位置。
示例代码
#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;
int main() {
vector<int> v1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 使用 shift_left 将元素向左移动
auto new_first1 = shift_left(v1.begin(), v1.begin() + 4, v1.end());
copy(v1.begin(), v1.end(), ostream_iterator<int>(cout, " "));
cout << "\nnew_first1 position: " << distance(v1.begin(), new_first1) << '\n';
// 使用 shift_right 将元素向右移动
vector<int> v2 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
auto new_last2 = shift_right(v2.begin(), v2.end(), v2.begin() + 5);
copy(v2.begin(), v2.end(), ostream_iterator<int>(cout, " "));
cout << "\nnew_last2 position: " << distance(v2.begin(), new_last2) << '\n';
return 0;
}
输出
5 6 7 8 9 1 2 3 4
new_first1 position: 5
5 6 7 8 9 1 2 3 4
new_last2 position: 5
说明
- 在
shift_left
示例中,{1, 2, 3, 4}
被移到v1
的末尾,剩余的{5, 6, 7, 8, 9}
被移动到前面。 - 在
shift_right
示例中,{5, 6, 7, 8, 9}
被移到v2
的前面,剩余的{1, 2, 3, 4}
被移动到后面。
shuffle
算法
头文件
#include <algorithm>
#include <random>
函数原型
template<class RandomAccessIterator, class RandomGenerator>
void shuffle(RandomAccessIterator first, RandomAccessIterator last, RandomGenerator&& g);
描述
shuffle
:打乱[first, last)
范围内元素的顺序。RandomGenerator
是一个随机数生成器(通常是std::mt19937
或std::default_random_engine
),用于生成随机索引,以便重新排列元素。
示例代码
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <random>
using namespace std;
int main() {
// 创建一个包含整数的 vector
vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 使用 std::random_device 和 std::mt19937 作为随机数生成器
random_device rd;
mt19937 g(rd());
// 打乱 vector 中的元素
shuffle(v.begin(), v.end(), g);
// 打印打乱后的元素
copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
cout << '\n';
return 0;
}
输出
3 8 1 6 5 9 4 7 2
说明
shuffle
函数通过使用随机数生成器g
对[first, last)
范围内的元素进行打乱。- 每次运行程序时,输出的元素顺序都可能不同,因为
shuffle
使用了随机生成器。
注意事项
- 要确保提供的
RandomGenerator
是有效的,并且能够生成足够的随机数来打乱元素。std::mt19937
是一种常用的选择。 shuffle
是就地打乱,不会创建新的容器,而是修改原容器中的元素顺序。
short_heap
算法
头文件
#include <algorithm>
函数原型
-
template<class RandomAccessIterator, class Compare>
void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
-
template<class RandomAccessIterator>
void make_heap(RandomAccessIterator first, RandomAccessIterator last);
-
template<class RandomAccessIterator, class Compare>
void push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
-
template<class RandomAccessIterator>
void push_heap(RandomAccessIterator first, RandomAccessIterator last);
-
template<class RandomAccessIterator, class Compare>
void pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
-
template<class RandomAccessIterator>
void pop_heap(RandomAccessIterator first, RandomAccessIterator last);
-
template<class RandomAccessIterator, class Compare>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
-
template<class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last);
描述
-
make_heap
: 将[first, last)
范围内的元素重新排列为一个堆(heap)。默认情况下使用operator<
,可以通过传入比较函数来指定自定义的堆顺序。 -
push_heap
: 将新元素加入堆中并调整堆以保持堆性质。last
参数是新元素的结束迭代器。 -
pop_heap
: 从堆中删除最大(或最小)元素,并调整堆。last
参数是当前堆的结束迭代器,删除的元素将位于last-1
。 -
sort_heap
: 将堆排序为一个有序的序列。可以使用自定义的比较函数进行排序。
示例代码
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {20, 10, 30, 5, 1, 15};
// 创建堆
make_heap(v.begin(), v.end());
cout << "Initial heap: ";
for (int n : v) cout << n << " ";
cout << '\n';
// 向堆中添加元素
v.push_back(25);
push_heap(v.begin(), v.end());
cout << "Heap after push: ";
for (int n : v) cout << n << " ";
cout << '\n';
// 从堆中删除元素
pop_heap(v.begin(), v.end());
v.pop_back();
cout << "Heap after pop: ";
for (int n : v) cout << n << " ";
cout << '\n';
// 堆排序
sort_heap(v.begin(), v.end());
cout << "Sorted heap: ";
for (int n : v) cout << n << " ";
cout << '\n';
return 0;
}
输出
Initial heap: 30 20 15 5 10 1
Heap after push: 30 20 25 5 10 1 15
Heap after pop: 25 20 15 5 10 1
Sorted heap: 1 5 10 15 20 25
说明
make_heap
将一个序列转换为堆。push_heap
和pop_heap
用于在堆中添加和删除元素,并保持堆的性质。sort_heap
将堆排序为有序序列。
注意事项
- 使用
std::greater
作为比较函数可以创建一个小根堆(最小值在顶部)。 - 堆操作的时间复杂度为 O(log N),而
sort_heap
的时间复杂度为 O(N log N)。
swap
/ swap_ranges
算法
头文件
#include <algorithm>
函数原型
-
void swap(Type &object1, Type &object2) noexcept;
-
void swap(Type (&object1)[N], Type (&object2)[N]) noexcept;
-
ForwardIterator2 swap_ranges([ExecPol,] ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 result);
描述
-
第一个原型: 交换
object1
和object2
的值。交换可以通过循环拷贝赋值或循环移动赋值(如果可用)来实现。 -
第二个原型: 交换
object1
和object2
两个数组中的元素,数组大小在编译时已知。 -
第三个原型: 将
[first1, last1)
范围中的元素与[result, result + (last1 - first1))
范围中的元素交换,函数返回值为result + (last1 - first1)
。这两个范围必须是互不重叠的。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <iterator>
using namespace std;
void show(string const *begin, string const *end) {
copy(begin, end, ostream_iterator<string>(cout, " "));
cout << '\n';
}
int main() {
string first[] = { "alpha", "bravo", "charley" };
string second[] = { "echo", "foxtrot", "golf" };
cout << "Before:\n";
show(first, end(first));
show(second, end(second));
// 逐个交换
for (size_t idx = 0; idx < size(first); ++idx)
swap(first[idx], second[idx]);
cout << "After:\n";
show(first, end(first));
show(second, end(second));
// 使用 swap_ranges 交换整个范围
swap_ranges(first, end(first), second);
cout << "After swap_ranges:\n";
show(first, end(first));
show(second, end(second));
return 0;
}
输出
Before:
alpha bravo charley
echo foxtrot golf
After:
echo foxtrot golf
alpha bravo charley
After swap_ranges:
alpha bravo charley
echo foxtrot golf
说明
swap
: 用于交换两个对象或两个相同大小的数组的值。swap_ranges
: 用于交换两个范围内的元素,范围长度相同,并且这两个范围必须是不重叠的。
通过使用这些算法,可以方便地交换数据,简化代码逻辑。
transform
算法
头文件
#include <algorithm>
函数原型
-
OutputIterator transform([ExecPol,] InputIterator first, InputIterator last, OutputIterator result, UnaryOperator op);
-
OutputIterator transform([ExecPol,] InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryOperator op);
描述
-
第一个原型: 对范围
[first, last)
中的每个元素应用一元操作符op
,并将结果存储到从result
开始的范围中。函数返回值指向生成元素的最后一个元素之后的位置。 -
第二个原型: 对范围
[first1, last1)
中的每个元素和从first2
开始的第二个范围中的对应元素应用二元操作符op
,将结果存储到从result
开始的范围中。函数返回值指向生成元素的最后一个元素之后的位置。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <cctype>
#include <iterator>
#include <vector>
#include <functional>
using namespace std;
string caps(string const &src) {
string tmp;
tmp.resize(src.length());
transform(src.begin(), src.end(), tmp.begin(), ::toupper);
return tmp;
}
int main() {
string words[] = {"alpha", "bravo", "charley"};
copy(words, transform(words, words + 3, words, caps), ostream_iterator<string>(cout, " "));
cout << '\n';
int values[] = {1, 2, 3, 4, 5};
vector<int> squares;
transform(values, values + 5, back_inserter(squares), multiplies<int>());
copy(squares.begin(), squares.end(), ostream_iterator<int>(cout, " "));
cout << '\n';
return 0;
}
输出
ALPHA BRAVO CHARLEY
1 4 9 16 25
说明
-
transform
:- 使用一元操作符
op
对每个元素进行转换,并将结果存储到目标范围中。 - 使用二元操作符
op
对两个范围内的对应元素进行操作,将结果存储到目标范围中。
- 使用一元操作符
-
与
for_each
的区别:transform
使用函数对象的返回值来生成新的元素,而不修改传递给函数对象的原始值。for_each
则直接修改传递给函数对象的引用参数。
-
范围基础的 for 循环:
- 可以用来代替
transform
算法,但transform
也可以用于子范围和反向迭代器的情况。
- 可以用来代替
transform_reduce
算法
头文件
#include <numeric>
函数原型
-
Type transform_reduce([ExecPol,] InputIterator first1, InputIterator last1, InputIterator first2, Type value);
-
Type transform_reduce([ExecPol,] InputIterator first1, InputIterator last1, InputIterator first2, Type value, BinaryOperation reduce, BinaryOperation transform);
-
Type transform_reduce([ExecPol,] InputIterator first1, InputIterator last1, Type value, BinaryOperation reduce, UnaryOperation transform);
描述
-
第一个原型: 等同于第二个原型,使用
std::plus<>
作为reduce
二元操作符,使用std::multiplies<>
作为transform
二元操作符。它也等同于inner_product
的并行版本。 -
第二个原型: 将
transform
二元操作符应用于范围[first1, last1)
中的每个元素(作为左操作数)和从first2
开始的范围中的对应元素(作为右操作数)。将计算结果作为右操作数,以value
作为左操作数应用reduce
二元操作符,最终返回reduce
的结果。 -
第三个原型: 将
transform
应用于范围[first1, last1)
中的每个元素,然后将value
和每个通过transform
返回的值传递给reduce
,最终返回reduce
的结果。
示例代码
#include <numeric>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <string>
using namespace std;
class Cat {
std::string d_sep;
public:
Cat(string const &sep) : d_sep(sep) {}
string operator()(string const &s1, string const &s2) const {
return s1 + d_sep + s2;
}
};
int main() {
size_t ia1[] = {1, 2, 3, 4, 5, 6, 7}; // 用于代替 inner_product:
cout << "The sum of all squares in ";
copy(ia1, ia1 + 7, ostream_iterator<size_t>{cout, " "});
cout << "is " << transform_reduce(ia1, ia1 + 7, ia1, 0) << '\n';
size_t ia2[] = {7, 6, 5, 4, 3, 2, 1};
cout << "The sum of all cross-products in ";
copy(ia1, ia1 + 7, ostream_iterator<size_t>{cout, " "});
cout << "and ";
copy(ia2, ia2 + 7, ostream_iterator<size_t>{cout, " "});
cout << "is " << transform_reduce(ia1, ia1 + 7, ia2, 0) << '\n';
string names1[] = {"Frank", "Karel", "Piet"};
string names2[] = {"Brokken", "Kubat", "Plomp"};
cout << "All combined names of ";
copy(names1, names1 + 3, ostream_iterator<string>{cout, " "});
cout << "and ";
copy(names2, names2 + 3, ostream_iterator<string>{cout, " "});
cout << "are: " <<
transform_reduce(names1, names1 + 3, names2, "\t"s,
Cat{ "\n\t"}, Cat{ " " }) << '\n';
return 0;
}
输出
The sum of all squares in 1 2 3 4 5 6 7 is 140
The sum of all cross-products in 1 2 3 4 5 6 7 and 7 6 5 4 3 2 1 is 84
All combined names of Frank Karel Piet and Brokken Kubat Plomp are:
Frank Brokken
Karel Kubat
Piet Plomp
说明
-
transform_reduce
:- 第一个原型和第二个原型通过使用
std::plus<>
和std::multiplies<>
进行加法和乘法操作,实现了类似inner_product
的功能。 - 第二个原型允许用户自定义
reduce
和transform
操作符。 - 第三个原型允许在对输入范围进行
transform
操作后,将value
和结果传递给reduce
。
- 第一个原型和第二个原型通过使用
-
与
for_each
和transform
的区别:transform_reduce
结合了transform
和reduce
操作,可以更高效地处理需要同时应用转换和归约的情况。
处理未初始化的内存
概述
如第9.1.5节所述,placement new 操作符用于在“原始内存”中安装值或对象,即内存已经可用但尚未初始化为预期的对象类型。在使用 auto ptr = new string{ "hello" }
时,字符串在专门分配给该对象的内存中构造,且对象类型的构造函数会在该内存中初始化对象。调用 delete ptr
时,字符串的析构函数会被调用,然后返回内存到通用内存池。
使用 placement new 时,存储对象的内存已经可用,通过 auto ptr = new (storageAddress) string{ "hello" }
仅在指定的 storageAddress
位置构造字符串。字符串可以通过 ptr
访问,但不能使用 delete ptr
,因为 storageAddress
位置的内存已在调用 placement new 操作符之前可用。因此,在这些情况下,需要显式调用对象的析构函数(使用 ptr->~string()
),而使用 delete ptr
是完全错误的,会导致内存错误并中止程序。
处理未初始化内存的通用算法
使用 placement new 时,可以使用以下通用算法来简化操作。这些算法在 <memory>
头文件中定义,支持执行策略,并提供了复制、填充、初始化和移动对象到/从未初始化(原始)内存的功能,以及删除存储在原始内存中的对象的功能。
-
uninitialized_copy([ExecPol,] ForwardIterator first, ForwardIterator last, ForwardIterator dest)
将[first, last)
范围内的元素复制到从dest
开始的原始内存中,返回超出最后一个复制元素的位置。 -
uninitialized_copy_n([ExecPol,] ForwardIterator first, size_t nObjects, ForwardIterator dest)
与上述算法相同,但复制nObjects
个元素。 -
uninitialized_default_construct([ExecPol,] ForwardIterator first, ForwardIterator last)
在[first, last)
范围内的原始内存位置安装默认构造的值。要求迭代器所指的类型要么是平凡类型(如内建类型),要么定义了value_type
返回其类型名称。对于平凡类型,安装的值不假定是 0 初始化的。 -
uninitialized_default_construct_n([ExecPol,] ForwardIterator first, size_t nObjects)
与上述算法相同,但在未初始化的内存中安装nObjects
个值。 -
uninitialized_fill([ExecPol,] ForwardIterator first, ForwardIterator last, Type const &value)
与uninitialized_copy
相似,但在未初始化内存中安装value
的副本。 -
uninitialized_fill([ExecPol,] ForwardIterator first, size_t nObjects, Type const &value)
与上述算法相同,但将value
复制到未初始化内存中的nObjects
个后续位置。 -
uninitialized_move([ExecPol,] ForwardIterator first, ForwardIterator last, ForwardIterator dest)
与uninitialized_copy
相同,但将[first, last)
范围内的元素移动到原始内存中。 -
uninitialized_move_n([ExecPol,] ForwardIterator first, size_t nObjects, ForwardIterator dest)
与上述算法相同,但移动nObjects
个元素。 -
uninitialized_value_construct([ExecPol,] ForwardIterator first, ForwardIterator last)
与uninitialized_default_construct
相似,但要求迭代器所指的类型定义了value_type
返回其类型名称。 -
uninitialized_value_construct_n([ExecPol,] ForwardIterator first, size_t nObjects)
与上述算法相同,但在未初始化的内存中安装nObjects
个值。 -
Type *construct_at(Type *raw, Args &&...args)
在原始内存raw
处构造类型Type
的对象,传递args...
给Type
的构造函数。
删除未初始化内存中的对象
-
void destroy([ExecPol,] ForwardIterator first, ForwardIterator last)
假设first
指向的类型,它对[first, last)
范围内的所有元素调用iterator->~Type()
。 -
void destroy([ExecPol,] ForwardIterator first, size_t nObjects)
与上述算法相同,但调用nObjects
个对象的析构函数。 -
void destroy_at(Type *raw)
调用原始内存raw
处对象的析构函数。如果raw
指向一个由 placement new 分配的对象数组,则调用数组中每个元素的析构函数。
示例代码
#include <memory>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
int main() {
char raw[4 * sizeof(string)]; // 用于接收字符串的原始内存
string *ptr = reinterpret_cast<string *>(raw); // 指向字符串的指针
// 在原始内存中构造 4 个字符串
uninitialized_default_construct_n(ptr, 4);
destroy(ptr, ptr + 4); // 调用字符串的析构函数
vector<string> vs(4, "string"); // 移动 4 个字符串到原始内存
uninitialized_move(vs.begin(), vs.end(), ptr);
cout << vs.front() << ", " << vs.back() << '\n';
cout << ptr[0] << ", " << ptr[3] << '\n';
destroy(ptr, ptr + 4); // 调用字符串的析构函数
}
输出
string, string
string, string
说明
uninitialized_copy
、uninitialized_move
等算法可以在未初始化的内存中进行对象的拷贝或移动,避免了直接操作原始内存时的复杂性。destroy
和destroy_at
函数用于显式调用对象的析构函数,确保正确清理已构造对象的资源。
unique
头文件
<algorithm>
函数原型
-
ForwardIterator unique([ExecPol,] ForwardIterator first, ForwardIterator last);
使用数据类型的operator==
,将[first, last)
范围内的所有连续相等元素中的除了第一个之外的其他元素移到范围的末尾。返回的前向迭代器标记了剩余元素的开始。所有[first, return-value)
范围内的元素都是唯一的,所有[return-value, last)
范围内的元素具有不确定(但有效)的值。 -
ForwardIterator unique([ExecPol,] ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
使用二元谓词pred
,将[first, last)
范围内的所有连续元素中,谓词pred
返回true
的元素(除了第一个)移到范围的末尾。谓词pred
期望两个参数,这两个参数是迭代器指向的数据类型。返回的前向迭代器标记了剩余元素的开始。在[first, return-value)
范围内的所有元素使pred
返回false
(即它们是唯一的)。所有剩余的元素(即[return-value, last)
范围内的元素)具有不确定(但有效)的值。 -
OutputIterator unique_copy([ExecPol,] InputIterator first, InputIterator last, OutputIterator result);
将[first, last)
范围内的唯一元素复制到从result
开始的输出范围中。返回的输出迭代器标记了超出最后一个复制元素的位置。 -
OutputIterator unique_copy([ExecPol,] InputIterator first, InputIterator last, OutputIterator result, BinaryPredicate pred);
使用二元谓词pred
,将[first, last)
范围内的唯一元素(谓词pred
返回false
的元素)复制到从result
开始的输出范围中。返回的输出迭代器标记了超出最后一个复制元素的位置。
描述
std::unique
通用算法假设范围内的元素已经排序(参见第19.1.54节)。
-
第一个原型:使用数据类型的
operator==
,将[first, last)
范围内的连续相等元素中的第一个之外的其他元素移到范围的末尾。返回的前向迭代器标记了剩余元素的开始。[first, return-value)
范围内的所有元素都是唯一的,而[return-value, last)
范围内的元素具有不确定(但有效)的值。 -
第二个原型:使用二元谓词
pred
,将[first, last)
范围内的连续元素中,谓词pred
返回true
的元素(除了第一个)移到范围的末尾。返回的前向迭代器标记了剩余元素的开始。在[first, return-value)
范围内的所有元素使pred
返回false
(即它们是唯一的)。所有剩余的元素(即[return-value, last)
范围内的元素)具有不确定(但有效)的值。
示例代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
#include <string>
using namespace std;
bool casestring(string const &first, string const &second) {
return strcasecmp(first.c_str(), second.c_str()) == 0;
}
int main() {
string words[] = {"alpha", "alpha", "Alpha", "papa", "quebec"};
size_t const size = sizeof(words) / sizeof(string);
string *removed = unique(words, words + size);
copy(words, removed, ostream_iterator<string>(cout, " "));
cout << '\n' << "Trailing elements are:\n";
copy(removed, words + size, ostream_iterator<string>(cout, " "));
cout << '\n';
removed = unique(words, words + size, casestring);
copy(words, removed, ostream_iterator<string>(cout, " "));
cout << '\n' << "Trailing elements are:\n";
copy(removed, words + size, ostream_iterator<string>(cout, " "));
cout << '\n';
}
输出
alpha Alpha papa quebec
Trailing elements are:
quebec
alpha papa quebec
Trailing elements are:
quebec quebec
说明
unique
:用于删除范围内的重复元素,只保留每个重复序列的第一个元素。可以使用默认的==
比较,也可以提供自定义的二元谓词进行比较。unique_copy
:与unique
类似,但不会在原始范围内修改元素,而是将唯一的元素复制到另一个范围中。
unique_copy
头文件
<algorithm>
函数原型
-
OutputIterator unique_copy([ExecPol,] InputIterator first, InputIterator last, OutputIterator result);
将[first, last)
范围内的元素复制到从result
开始的目标容器中。连续相等的元素(使用数据类型的operator==
)仅复制一次(保留重复序列中的第一个元素)。返回的输出迭代器指向最后一个复制元素的下一个位置。 -
OutputIterator unique_copy([ExecPol,] InputIterator first, InputIterator last, OutputIterator result, BinaryPredicate pred);
将[first, last)
范围内的元素复制到从result
开始的目标容器中。连续的元素中,谓词pred
返回true
的元素仅复制一次(保留重复序列中的第一个元素)。返回的输出迭代器指向最后一个复制元素的下一个位置。
描述
-
第一个原型:将
[first, last)
范围内的元素复制到目标容器中,从result
开始。连续相等的元素(使用数据类型的operator==
)仅复制一次(保留连续相等元素中的第一个)。返回的输出迭代器指向最后一个复制元素的下一个位置。 -
第二个原型:将
[first, last)
范围内的元素复制到目标容器中,从result
开始。连续的元素中,对于谓词pred
返回true
的元素仅复制一次(保留连续相等元素中的第一个)。返回的输出迭代器指向最后一个复制元素的下一个位置。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <cstring>
using namespace std;
bool casestring(string const &first, string const &second) {
return strcasecmp(first.c_str(), second.c_str()) == 0;
}
int main() {
string words[] = {"oscar", "Alpha", "alpha", "alpha", "papa", "quebec"};
size_t const size = sizeof(words) / sizeof(string);
vector<string> remaining;
unique_copy(words, words + size, back_inserter(remaining));
copy(remaining.begin(), remaining.end(), ostream_iterator<string>(cout, " "));
cout << '\n';
vector<string> remaining2;
unique_copy(words, words + size, back_inserter(remaining2), casestring);
copy(remaining2.begin(), remaining2.end(), ostream_iterator<string>(cout, " "));
cout << '\n';
}
输出
oscar Alpha alpha papa quebec
oscar Alpha papa quebec
说明
unique_copy
:用于将范围内的唯一元素复制到另一个容器中。相邻相等的元素(默认使用==
比较)只复制第一个。可以通过提供自定义的二元谓词来控制相等元素的比较。
upper_bound
头文件
<algorithm>
函数原型
-
ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, Type const &value);
在[first, last)
范围内的有序元素(按升序排序)中,搜索第一个大于value
的元素。返回的迭代器指向可以插入value
的位置,而不会破坏元素的有序性。若没有找到这样的元素,则返回last
。 -
ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, Type const &value, Compare comp);
在[first, last)
范围内的元素必须按comp
函数或函数对象排序。comp
函数用于将每个元素与value
进行比较。返回的迭代器指向第一个comp
函数应用于元素和value
返回true
的位置。注意:与lower_bound
不同,comp
函数的参数顺序在upper_bound
中与lower_bound
相反。
描述
-
第一个原型:用于按升序排序的元素范围。返回的迭代器指向第一个大于
value
的位置。如果没有找到这样的元素,则返回last
。 -
第二个原型:用于按
comp
函数排序的元素范围。返回的迭代器指向第一个comp
函数返回true
的位置。如果没有找到这样的元素,则返回last
。
示例代码
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <cstring>
using namespace std;
int main() {
using pic = pair<int, char>;
pic picArr[] = {{1, 'f'}, {5, 'r'}, {5, 'a'}, {7, 'n'}, {8, 'k'}};
pic *picArrEnd = picArr + size(picArr);
cout << "Sequence: ";
for (auto &pair : picArr)
cout << '{' << pair.first << ',' << pair.second << "}, ";
cout << '\n';
// 使用自定义比较函数查找 lower_bound 和 upper_bound
auto iter = lower_bound(picArr, picArrEnd, 5,
[&](pic const &range, int value) {
return range.first < value;
});
cout << "lower_bound, <, {5,?} can be inserted before {" << iter->first << ',' << iter->second << "}\n";
iter = upper_bound(picArr, picArrEnd, 5,
[&](int value, pic const &range) {
return value < range.first;
});
cout << "upper_bound, <, {5,?} can be inserted before {" << iter->first << ',' << iter->second << "}\n";
iter = upper_bound(picArr, picArrEnd, 9,
[&](int value, pic const &range) {
return value < range.first;
});
cout << "upper_bound, <, {9,?} can be inserted "
<< (&*iter == picArrEnd ? "at the end" : "???") << '\n';
// 按降序排序后再次使用 lower_bound 和 upper_bound
sort(picArr, picArrEnd,
[](pic const &lhs, pic const &rhs) {
return lhs.first > rhs.first;
});
cout << "\nSequence: ";
for (auto &pair: picArr)
cout << '{' << pair.first << ',' << pair.second << "}, ";
cout << '\n';
iter = lower_bound(picArr, picArrEnd, 5,
[&](pic const &range, int value) {
return range.first > value;
});
cout << "lower_bound, >, {5,?} can be inserted before {" << iter->first << ',' << iter->second << "}\n";
iter = upper_bound(picArr, picArrEnd, 5,
[&](int value, pic const &range) {
return value > range.first;
});
cout << "upper_bound, >, {5,?} can be inserted before {" << iter->first << ',' << iter->second << "}\n";
iter = upper_bound(picArr, picArrEnd, 0,
[&](int value, pic const &range) {
return value > range.first;
});
cout << "upper_bound, >, {0,?} can be inserted "
<< (&*iter == picArrEnd ? "at the end" : "???") << '\n';
}
输出
Sequence: {1,f}, {5,r}, {5,a}, {7,n}, {8,k},
lower_bound, <, {5,?} can be inserted before {5,r}
upper_bound, <, {5,?} can be inserted before {7,n}
upper_bound, <, {9,?} can be inserted at the end
Sequence: {8,k}, {7,n}, {5,r}, {5,a}, {1,f},
lower_bound, >, {5,?} can be inserted before {5,r}
upper_bound, >, {5,?} can be inserted before {1,f}
upper_bound, >, {0,?} can be inserted at the end
说明
- 第 7 行到第 12 行:
picArr
数组中的元素按其第一个成员进行排序。 - 第 18 行到第 23 行:使用自定义比较函数调用
lower_bound
。注意第 19 行中,lambda 表达式的第一个参数是范围内的值,而第二个参数是目标值。 - 第 27 行到第 32 行:使用自定义比较函数调用
upper_bound
。与lower_bound
不同,lambda 表达式的第一个参数是目标值,而第二个参数是范围内的值。 - 第 57 行到第 80 行:对
picArr
数组进行降序排序后,使用lower_bound
和upper_bound
。此时应使用>
运算符代替<
运算符。
堆算法
堆是一种可以用数组表示的二叉树。在标准堆中,元素的键值不小于其子节点的键值。这种堆称为最大堆(max heap)。如图 19.1 所示,包含键值的树可以按照以下方式组织。这样的树也可以组织成数组形式:
12, 11, 10, 8, 9, 7, 6, 1, 2, 4, 3, 5
在以下描述中,请记住两个指针:一个指向树的下一个节点,另一个指向下一个节点的子节点。最初,node
指向第一个元素,child
指向第二个元素。
∗node++ (== 12)
:12 是根节点,它的子节点是∗child++ (11)
和∗child++ (10)
,都小于 12。- 下一个节点
∗node++ (== 11)
:它的子节点是∗child++ (8)
和∗child++ (9)
。 - 下一个节点
∗node++ (== 10)
:它的子节点是∗child++ (7)
和∗child++ (6)
。 - 下一个节点
∗node++ (== 8)
:它的子节点是∗child++ (1)
和∗child++ (2)
。 - 节点
∗node++ (== 9)
:它的子节点是∗child++ (4)
和∗child++ (3)
。 - 最后(考虑到子节点):
∗node++ (== 7)
只有一个子节点∗child++ (5)
。由于child
现在指向数组之外,剩余的节点没有子节点。因此,节点 6、1、2、4、3 和 5 没有子节点。
注意,左支和右支的顺序并不一致:8 小于 9,但 7 大于 6。
堆是通过逐层遍历二叉树来创建的,从顶节点开始。顶节点是 12,位于第零层。在第一层我们找到 11 和 10。在第二层找到 8、9、7 和 6,依此类推。
堆可以在支持随机访问的容器中构建。因此,列表不适合作为堆的数据结构。堆可以从一个(未排序的)数组中构建(使用 make_heap
)。可以从堆中删除顶元素,然后重新排序堆(使用 pop_heap
),可以向堆中添加新元素,然后重新排序堆(使用 push_heap
),还可以对堆中的元素进行排序(使用 sort_heap
,这会使堆失效)。
make_heap
函数
-
头文件:
<algorithm>
-
函数原型:
void make_heap(RandomAccessIterator first, RandomAccessIterator last);
void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
-
描述:
- 第一个原型: 将范围
[first, last)
中的元素重新排序,形成一个最大堆(max-heap),使用数据类型的operator<
进行比较。 - 第二个原型: 将范围
[first, last)
中的元素重新排序,形成一个最大堆(max-heap),使用二进制比较函数对象comp
进行比较。
- 第一个原型: 将范围
push_heap
函数
-
头文件:
<algorithm>
-
函数原型:
void push_heap(RandomAccessIterator first, RandomAccessIterator last);
void push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
-
描述:
- 第一个原型: 假设范围
[first, last - 1)
已经包含一个有效的堆,并且last - 1
位置的元素是要添加到堆中的新元素,则将范围[first, last - 1)
中的元素重新排序,形成一个最大堆(max-heap),使用数据类型的operator<
进行比较。 - 第二个原型: 假设范围
[first, last - 1)
已经包含一个有效的堆,并且last - 1
位置的元素是要添加到堆中的新元素,则将范围[first, last - 1)
中的元素重新排序,形成一个最大堆(max-heap),使用二进制比较函数对象comp
进行比较。
- 第一个原型: 假设范围
sort_heap
函数
-
头文件:
<algorithm>
-
函数原型:
void sort_heap(RandomAccessIterator first, RandomAccessIterator last);
void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
-
描述:
- 第一个原型: 假设范围
[first, last)
中的元素构成一个有效的最大堆(max-heap),则将范围[first, last)
中的元素进行排序,使用数据类型的operator<
进行比较。 - 第二个原型: 假设范围
[first, last)
中的元素构成一个有效的堆,则将范围[first, last)
中的元素进行排序,使用二进制比较函数对象comp
进行比较。
- 第一个原型: 假设范围
使用堆函数的示例
以下是一个示例,展示了如何使用各种堆操作算法:
#include <algorithm>
#include <iostream>
#include <functional>
#include <iterator>
using namespace std;
void show(int *ia, char const *header){
cout << header << ":\n";
copy(ia, ia + 20, ostream_iterator<int>(cout, " "));
cout << '\n';
}
int main()
{
int ia[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
// 将数组转化为最大堆
make_heap(ia, ia + 20);
show(ia, "The values 1-20 in a max-heap");
// 从堆中移除第一个元素(现在在末尾)
pop_heap(ia, ia + 20);
show(ia, "Removing the first element (now at the end)");
// 将20(在末尾)重新加入堆
push_heap(ia, ia + 20);
show(ia, "Adding 20 (at the end) to the heap again");
// 对堆中的元素进行排序
sort_heap(ia, ia + 20);
show(ia, "Sorting the elements in the heap");
// 使用 greater<int> 重新构建堆
make_heap(ia, ia + 20, greater<int>());
show(ia, "The values 1-20 in a heap, using > (and beyond too)");
// 从堆中移除第一个元素(现在在末尾)
pop_heap(ia, ia + 20, greater<int>());
show(ia, "Removing the first element (now at the end)");
// 重新将被移除的元素加入堆
push_heap(ia, ia + 20, greater<int>());
show(ia, "Re-adding the removed element");
// 对堆中的元素进行排序
sort_heap(ia, ia + 20, greater<int>());
show(ia, "Sorting the elements in the heap");
}
显示的结果:
The values 1-20 in a max-heap:
20 19 15 18 11 13 14 17 9 10 2 12 6 3 7 16 8 4 1 5
Removing the first element (now at the end):
19 18 15 17 11 13 14 16 9 10 2 12 6 3 7 5 8 4 1 20
Adding 20 (at the end) to the heap again:
20 19 15 17 18 13 14 16 9 11 2 12 6 3 7 5 8 4 1 10
Sorting the elements in the heap:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
The values 1-20 in a heap, using > (and beyond too):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Removing the first element (now at the end):
2 4 3 8 5 6 7 16 9 10 11 12 13 14 15 20 17 18 19 1
Re-adding the removed element:
1 2 3 8 4 6 7 16 9 5 11 12 13 14 15 20 17 18 19 10
Sorting the elements in the heap:
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1