swap_ranges
find
find_if
find_if_not
find_first_of
adjacent_find
find_end
search
search_n
binary_search
lower_bound([不小于第三个参数]的第一个元素)
upper_bound([大于第三个参数]的第一个元素)
equel_range(可以找出有序序列中所有和给定元素相等的元素)
partition
partition_copy
equal
mismatch
(1)C++ swap_ranges(STL swap_ranges)函数使用详解
可以用 swap_ranges() 算法来交换两个序列。这个算法需要 3 个正向迭代器作为参数。前两个参数分别是第一个序列的开始和结束迭代器,第三个参数是第二个序列的开始迭代器。
显然,这两个序列的长度必须相同。这个算法会返回一个迭代器,它指向第二个序列的最后一个被交换元素的下一个位置。例如:
using Name = std::pair<string, string>; // First and second name
std::vector<Name> people {Name{"Al", "Bedo" }, Name { "Ann", "Ounce"}, Name{"Jo","King"}};
std::list<Name> folks {Name{"Stan", "Down"}, Name{"Dan","Druff"},Name {"Bea", "Gone"}};
std::swap_ranges(std::begin(people), std::begin(people) + 2, ++std::begin(folks));
std::for_each(std::begin(people), std::end(people),[](const Name& name) {std: :cout << '"' << name.first << " " << name.second << "\" ";});
std::cout << std::endl; // "Dan Druff" "Bea Gone" "Jo King"
std::for_each (std::begin (folks) , std::end (folks) ,[] (const Name& name){std::cout << '"' << name.first << " " << name.second << "\" "; });
std::cout << std::endl;// "Stan Down" "Al Bedo" "Ann Ounce"
这里使用 vector 和 list 容器来保存 pair<string,string> 类型的元素,pair<string,string> 用来表示名称。swap_ranges() 算法被用来交换 people 的前两个元素和 folks
的后两个元素。这里并没有为了将 pair 对象写入流而重载 operator<<() 函数,因此 copy() 无法用输出流迭代器来列出容器的内容。为了生成输出,选择使用 for_each() 算法将 lambda 表达式运用到容器的每个元素上。这个 lambda 表达式只会将传给它的 Name 元素的成员变量写入标准输出流。注释展示了执行这段代码后输出的结果:
定义在 utility 头文件中的 swap() 算法的重载函数的模板原型为:
template<typename T1, typename T2> void swap(std::pair<T1,T2> left, std::pair<T1,T2> right);
这段代码会对 pair<T1,T2> 对象进行交换,在前面的代码段中也可以用 swap_ranges() 来交换元素。
用来交换两个 T 类型对象的 swap() 模板也被定义在 utility 头文件中。除了 pair 对象的重载之外,utility 文件头中也有可以交换任何类型的容器对象的模板的重载。
也就是说,可以交换两个 list<T> 容器或者两个 set<T> 容器但不能是一个 list<T> 和 vector<T>,也不能是一个 list<T1> 和一个 list<T2>。
(2)C++ find(STL find)查找算法详解
find() 为在输入迭代器所定义的范围内查找单个对象的算法,它可以在前两个参数指定的范围内查找和第三个参数相等的第一个对象。
find 算法会返回一个指向被找到对象的迭代器,如果没有找到对象,会返回这个序列的结束迭代器。下面展示了如何使用 find():
std::vector<int> numbers {5, 46, -5, -6, 23, 17, 5, 9, 6, 5};
int value {23};
auto iter = std::find(std::begin(numbers), std::end(numbers), value);
if (iter != std:: end (numbers))
std::cout << value << " was found. \n";
这段代码会输出一条在 numbers 中找到 23 的消息,当然,可以反复调用 find() 来找出这个序列中所有给定元素的匹配项:
size_t count {};
int five {5};
auto start_iter = std::begin(numbers);
auto end_iter = std::end(numbers);
while((start_iter = std::find(start_iter, end_iter, five)) != end_iter)
{
++count;
++start_iter;
}
std::cout << five << " was found " << count << " times." << std::endl; // 3 times
在 while 循环中,count 变量会通过自增来记录 five 在 vector 容器 numbers 中的发现次数。循环表达式调用 find(),在 start_iter 和 end_iter 定义的
范围内查找 five。find() 返回的迭代器被保存在 start_ter 中,它会覆盖这个变量先前的值。最初,find() 会搜索 numbers 中的所有元素,因此 find() 会返回
一个指向 five 的第一个匹配项的迭代器。
每次找到 five,循环体中的startjter都会自增,因此它会指向被找到元素的后一个元素。所以,下一次遍历搜索的范围是从这个位置到序列末尾。当不再能够找到 five 时,
find() 会返回 end_iter,循环结束。
(3)C++ find_if(STL find_if)查找算法详解
find_if() 同 find() 一样,为在输入迭代器所定义的范围内查找单个对象的算法,它可以在前两个参数指定的范围内查找可以使第三个参数指定的谓词返回 true 的第一个对象。
谓词不能修改传给它的对象。
find_if() 会返回一个指向被找到对象的迭代器,如果没有找到对象,会返回这个 序列的结束迭代器。
可以按如下方式使用 find_if() 来查找 numbers 中第一个大于 value 的元素:
int value {5};
auto iter1 = std::find_if(std::begin(numbers), std::end(numbers),[value](int n) { return n > value; });
if(iter1 != std::end(numbers))
std::cout << *iter1 << " was found greater than " << value << ".\n";
find_if() 的第三个参数是一个 lambda 表达式的谓词。这个 lambda 表达式以值的方式捕获 value,并在 lambda 参数大于 value 时返回 true。这段代码会找到一个值为 46 的元素。
(4)C++ find_if_not(STL find_if_not)查找算法详解
find_if_not() 可以在输入迭代器所定义的范围内查找单个对象,该算法会在前两个参数指定的范围内查找可以使第三个参数指定的谓词 返回 false 的第一个对象。谓词不能修改传给它的对象。
find_if_not() 会返回一个指向被找到对象的迭代器,如果没有找到对象,会返回这个序列的结束迭代器。可以按如下方式用 find_if_not() 算法来查找使谓词为 false 的元素。
size_t count {}; int five {5};
auto start_iter = std::begin(numbers);
auto end_iter = std::end(numbers);
while((start_iter = std::find_if_not(start_iter, end_iter,[five](int n) {return n > five; })) != end_iter)
{
++count;
++start_iter;
}
std::cout << count << " elements were found that are not greater than "<< five << std::endl;
find_if_not() 的第三个参数是一个谓词,它和先前在 find_if() 算法中使用的 lambda 表达式相似。当元素大于 five 时,这个函数才会返回 true。当谓词返回 false 时,就找到了一个元素,
所以这个操作可以用来查找小于或等于 five 的元素。这段代码会找到 5 个元素,它们分别是 5、-5、-6、5、5。
(5)C++ find_first_of(STL find_first_of)查找算法详解
find_first_of() 算法可以在第一个序列中搜索第二个序列在第一个序列中第一次出现的任何元序列被搜索的范围由输入迭代器指定,但用于确定搜索序列的迭代器至少是正向迭代器。用 == 运算符来比较这两个序列中的元素,所以如果序列中是类类型的对象,这个类必须实现 operator==()。下面是一个使用 find_first_of() 的示例:
string text {"The world of searching"};
string vowels {"aeiou"};
auto iter = std::find_first_of (std::begin (text), std:: end (text), std::begin (vowels),std::end(vowels));
if(iter != std::end(text))
std::cout << "We found '" << *iter << "'." << std::endl; //We found 'e'.
这段代码会在 text 中查找第一次出现的 vowels 中的任意字符。在这个示例中,返回的迭代器指向“The”的第三个字母。
另一个版本的 find_first_of() 可以让我们在第二个序列中搜索指定范围内的,可以使第 5 个参数指定的二元谓词返回 true 的元素。这个序列中的元素不必是同种类型。
当这个 == 运算符不支持所有这些元素的比较时,就可以用这个版本的算法来定义相等比较,但也可以用其他的方式来定义。例如:
std::vector<long> numbers{64L, 46L, -65L, -128L, 121L, 17L, 35L, 9L, 91L, 5L};
int factors[] {7, 11, 13};
auto iter = std::find_first_of(std::begin(numbers),std::end(numbers),std::begin(factors), std::end(factors),[](long v, long d) { return v % d == 0; });// Predicate - true for a match
if (iter != std::end (numbers))
std::cout << *iter << " was found." << std::endl;
这个谓词是一个 lambda 表达式,当第一个参数可以被第二个参数整除时,它返回 true。
所以这段代码会找到 -65,因为这是 numbers 中第一个可以被 factors 数组中的元素 13 整除的元素。断言中的参数类型可以和序列元素的类型不同,
只要每个序列中的元素可以隐式转换为参数所对应的类型。在这里,factors 中的元素可以隐式转换为 long 类型。
[6]C++ adjacent_find(STL adjacent_find)算法使用详解 //入参为一个序列
adjacent_find() 算法可以用来搜索序列中两个连续相等的元素。用 == 运算符来比较连续的一对元素,返回的迭代器指向前两个相等元素中的第一个。如果没有一对相等的元素,这个算法返回这个序列的结束迭代器。例如:
string saying {"Children should be seen and not heard."};
auto iter = std::adjacent_find(std::begin(saying), std::end(saying));
if (iter != std::end(saying))
std::cout <<"In the following text: \n\""<< saying << "\"\n'"<< *iter << "' is repeated starting at index position "<<
std::distance(std::begin(saying), iter) << std::endl;
这里会搜索 saying 字符串的前两个相等字符,所以这段代码的输出如下:
In the following text:
"Children should be seen and not heard."
'e' is repeated starting at index position 20
adjacent_find() 算法的第二个版本,允许我们提供一个应用于连续元素的谓词。下面展示了如何用这个函数来查找这个序列中第一对都为奇数的连续整数:
std::vector<long> numbers {64L, 46L, -65L, -128L, 121L, l7L, 35L, 9L, 91L, 5L};
auto iter = std::adjacent_find(std::begin(numbers),std::end(numbers),[](long n1, long n2){ return n1 % 2 && n2 % 2; });
if(iter != std::end(numbers))
std::cout <<"The first pair of odd numbers is "<< *iter << " and " << *(iter+1) << std::endl;
当两个参数都不能被 2 整除时,这个 lambda 表达式就返回 true,所以这段代码会找到 121 和 17。
[7]C++ find_end(STL find_end)算法详解 //针对2个序列
find_end() 会在一个序列中查找最后一个和另一个元素段匹配的匹配项,也可以看作在一个元素序列中查找子序列的最后一个匹配项。这个算法会返回一个指向子序列的
最后一个匹配项的第一个元素的迭代器,或是一个指向这个序列的结束迭代器。下面是一个示例:
string text {"Smith, where Jones had had \"had\", had had \"had had\"." " \"Had had\" had had the examiners\' approval."};
std::cout << text << std::endl;
string phrase {"had had"};
auto iter = std::find_end(std::begin(text), std::end(text), std::begin (phrase),std::end(phrase));
if(iter != std::end(text))
std::cout << "The last \"" << phrase<< "\" was found at index "<< std::distance (std::begin (text), iter) << std::endl;
这段代码会从 text 中搜索“had had”的最后一个匹配项,并输出如下内容:
Smith, where Jones had had "had", had had "had had" . "Had had" had had the examiners' approval.
The last "had had" was found at index 63
另一个版本的 find_end() 接受一个用来比较元素的二元谓词作为第 5 个参数,可以用它来重复前面的搜索并忽“大小写:
size_t count {};
auto iter = std::end(text);
auto end_iter = iter;
while((iter = std::find_end(std::begin(text), end_iter, std::begin(phrase), std::end (phrase) ,
[] (char ch1, char ch2) { return std::toupper (ch1) == std::toupper(ch2);})) != end_iter)
{
++count;
end_iter = iter;
}
在将字符转换为大写后,会按对比较这两个序列中的元素。在 text 中会找到 phrase 的 5 个实例,因为找到的"Had had"也和 phrase 相等。
[8]C++ search(STL search)算法详解 //针对2个序列
在查找序列的子序列方面,search() 算法和 find_end() 算法相似,但它所查找的是第一个匹配项而不是最后一个。
和 find_end() 算法一样,它也有两个版本,第二个版本接受用来比较元素的谓词作为第 5 个参数。可以用 search() 来验证前面使用 find_end() 搜索的结果。
如何改变每次遍历搜索的具体范围是它们的主要不同之处。下面是一个示例:
纯文本复制
string text {"Smith, where Jones had had \"had\", had had \"had had\"."" \"Had had\" had had the examiners\' approval."};
std::cout << text << std::endl;
string phrase {"had had"};
size_t count {};
auto iter = std::begin(text);
auto end_iter = end(text);
while((iter = std::search(iter, end_iter, std::begin(phrase), std::end (phrase) ,
[](char ch1, char ch2) { return std::toupper (ch1) == std:: toupper (ch2); })) != end_iter)
{
++count;
std::advance(iter, phrase.size()); // Move to beyond end of subsequence found 注:std::advance(iter, phrase.size())也是个很有用的泛型算法
}
std::cout << "\n\""<< phrase << "\" was found "<< count << " times." << std::endl;
这段代码执行后会输出下面的内容:
Smith, where Jones had had "had", had had "had had". "Had had" had had the examiners' approval.
"had had" was found 5 times.
我们仍然忽略大小写来搜索“had had”,但会正向查找第一个匹配项。search() 算法返回的迭代器指向找到的子序列的第一个元素,因此为了搜索 phrase 的第二个实例,
iter 必须增加 phrase 中元素的个数,使它指向下一个找到的序列的第一个元素。
[9] C++ search_n(STL search_n)搜索算法详解 //针对1个序列
search_n() 算法会搜索给定元素的匹配项,它在序列中连续出现了给定的次数。它的前两个参数是定义搜索范围的正向迭代器,第 3 个参数是想要查找的第 4 个元素的连续匹配次数。例如:
std::vector<double> values {2.7, 2.7, 2.7, 3.14, 3.14, 3.14, 2.7, 2.7};
double value {3.14};
int times {3};
auto iter = std::search_n(std::begin(values), std::end(values), times, value);
if (iter != std::end(values))
std::cout << times << " successive instances of " << value<< " found starting index " << std::distance (std::begin (values), iter) <<std::endl;
这段代码会在 values 容器中查找第一个有 3 个 value 实例的匹配项。它找到的序列的索引为 3。注意用来指定个数的第三个参数必须是无符号整型;如果不是,编译这段代码会产生警告。
// Searching using search_n() to find freezing months
#include <iostream> // For standard streams
#include <vector> // For vector container
#include <algorithm> // For search_n()
#include <string> // For string class
using std::string;
int main()
{
std::vector<int> temperatures {65, 75, 56, 48, 31, 28, 32, 29, 40, 41, 44, 50};
int max_temp {32};
int times {3};
auto iter = std::search_n(std::begin(temperatures), std::end(temperatures), times, max_temp,[](double v, double max){return v <= max; });
std::vector<string> months {"January", "February", "March", "April", "May", "June","July", "August", "September", "October", "November", "December"};
if(iter != std::end(temperatures))
std::cout << "It was " << max_temp << " degrees or below for " << times<< " months starting in " <<
months[std::distance(std::begin(temperatures), iter)]<< std::endl;
}
temperatures 容器中保存了一年中每个月份的平均温度。search_n() 的最后一个参数是一个 lambda 表达式的谓词,当元素小于等于 max_temp 时,它会返回 true。month 容器中保存月份的名称。
[10] C++ binary_search(STL binary_search)二分查找算法详解
目前为止,你在本章中见到的搜索算法都是对序列进行顺序搜索,而且没有事先对元素进行排序的要求。二分查找一般比顺序搜索要快,但要求序列中的元素是有序的。
binary_search()算法
正如你们所想的那样,binary_search() 实现了一个二分查找算法。它会在前两个参数指定范围内搜索等同于第三个参数的元素。指定范围的迭代器必须是正向迭代器而且元素必须可以
使用 < 运算符来比较。这个序列中的元素必须被排成升序序列或者至少相对于所查找元素是有序的。如果找到第三个参数,这个算法会返回布尔值 true,否则返回 false。
所以,binary_search() 能告诉我们元素是否在这个序列中,但当它在序列中时,却不能告诉我们它的位置。 当然,如果必须知道它的位置,可以使用前面介绍的查找算法或 lower_bound()、
upper_bound() 或 equal_range(}。下面是一个使用 binary_search() 的示例:
std::list<int> values {17, 11, 40, 36, 22, 54, 48, 70, 61, 82, 78, 89, 99, 92, 43};
values.sort (); // Sort into ascending sequence
int wanted {22}; // What we are looking for
if(std::binary_search(std::begin(values), std::end(values), wanted))
std::cout << wanted << " is definitely in there - somewhere..."<< std::endl;
else
std::cout << wanted << " cannot be found - maybe you got it wrong..." << std::endl;
这里用 list 来保存一个任意顺序的任意值的集合,目的是为了提醒你记住这个容器的用法。这段代码使用 binary_search() 算法来查找期望的值。因为 binary_search()
只能使用有序序列,所以首先我们确保 list 中的元素是有序的。不能对 list 容器中的元素应用 sort() 算法,因为它需要的是随机访问迭代器,而 list 容器只提供了双向迭代器。
因为这个 list 定义了一个成员函数 sort(),可以将全部的元素排成升序,所以可以用这个函数来对容器中的元素进行排序。当这段代码执行时,会输出是否找到希望值的消息。
另一个版本的 binary_search() 接受一个额外的参数,它是一个用于查找元素的函数对象;显然,它必须和用于对被查找序列进行排序的比较操作有相同的效果。下面是一个演示如何
将值排成降序,然后查找期望值的示例:
std::list<int> values {17, 11, 40, 36, 22, 54, 48, 70, 61, 82, 78, 89, 99, 92, 43};
auto predicate = [](int a, int b){ return a > b;};
values.sort(predicate);
int wanted {22};
if (std::binary_search (std::begin (values), std::end (values), wanted, predicate))
std::cout << wanted << " is definitely in there - somewhere..."<< std:: endl;
else
std::cout << wanted << " cannot be found - maybe you got it wrong..."<< std::endl;
这里使用的 list 容器的成员函数 sort() 接受一个定义比较的函数对象作为参数,这里它是由一个 lambda 表达式定义的。同一个 lambda 表达式也被作
为 binary_searcli() 的第 4 个参数。当然,结果和前一段代码相同。
[11]C++ lower_bound(STL lower_bound)二分查找算法详解
lower_bound() 算法可以在前两个参数指定的范围内查找[不小于第三个参数]的第一个元素,也就是说大于等于第三个参数的第一个元素。前两个参数必须是正向迭代器。例如:
std::list<int> values {17, 11, 40, 36, 22, 54, 48, 70, 61, 82, 78, 89, 43};
values.sort(); // Sort into ascending sequence
int wanted {22}; // What we are looking for
std::cout << "The lower bound for "<< wanted<< " is "<< *std:: lower_bound (std::begin (values), std::end(values), wanted)<< std::endl;
运行结果为:
The lower bound for 22 is 22
从 list 容器的整数中可以看出算法正像我们所描述的那样工作。该算法还有额外的版本,它接受一个函数对象作为第三个参数,用于指定序列排序所使用的比较。
[12] C++ upper_bound(STL upper_bound)二分查找算法详解
upper_bound() 算法会在前两个参数定义的范围内查找[大于第三个参数]的第一个元素。对于这两个算法,它们所查找的序列都必须是有序的,而且它们被假定是使用 < 运算符来排序的。例如:
std::list<int> values {17, 11, 40, 36, 22, 54, 48, 70, 61, 82, 78, 89, 99, 92, 43};
values.sort (); // Sort into ascending sequence
int wanted {22}; // What we are looking for
std::cout<<"The upper bound for " << wanted<< " is " << *std::upper_bound (std::begin (values), std:: end (values), wanted)<< std::endl;
输出结果为:
The upper bound for 22 is 36
从 list 容器的整数中可以看出算法正像我们所描述的那样工作。该算法还有额外 的版本,它接受一个函数对象作为第三个参数,用于指定序列排序所使用的比较。
[13]C++ equel_range(STL equal_range)二分查找算法详解 // = lower_bound + upper_bound
(http://c.biancheng.net/view/589.html)
equal_range() 可以找出有序序列中所有和给定元素相等的元素。它的前两个参数是指定序列的两个正向迭代器,第三个参数是要查找的元素。这个算法会返回一个 pair 对象,
它有两个正向迭代器成员,其中的 first 指向的是不小于第三个参数的一个元素,second 指向大于第三个参数的一个元素,所以我们也可以通过在单个调用中调用 lower_bound()
和 upper_bound() 来得到同样的结果。可以用下面这些语句来替换前一个代码段中的两条输出语句:
auto pr = std::equal_range(std::begin(values) , std::end(values), wanted);
std::cout << "the lower bound for " << wanted << " is " << *pr.first << std::endl;
std::cout << "Tthe upper bound for " << wanted << " is " << *pr.second << std::endl;
它和前一段代码的输出完全相同。和前面的二分查找算法一样,equal_range() 也有一个有额外参数的版本,这个参数可以为有序序列提供一些不同于 < 运算符的比较。
前面说过,本节的算法要求它们所处理的序列的元素是有序的,但这并不是全部。所有的二分查找算法都可以用于以特殊方式分区的序列。对于一个给定的希望值,序列中
的元素必须按照 (element < wanted) 和 !(wanted < element) 来分区。可以用 equal_range() 二分查找算法来做这些工作,在对 values 容器中的元素执行
equal_range() 之前,可以按如下方式对它进行分区:
std::list<int> values {17, 11, 40, 36, 22, 54, 48, 70, 61, 82, 78, 89, 99, 92, 43};
// Output the elements in original order
std::copy(std::begin(values), std::end(values),std::ostream_iterator<int> {std::cout, " "});
std::cout << std::endl;
int wanted {22}; // What we are looking for
std::partition(std::begin(values), std::end(values),[wanted](double value) { return value < wanted; });
std::partition(std::begin(values), std::end(values),[wanted](double value) { return !(wanted < value); });
//Output the elements after partitioning
std::copy(std::begin(values), std::end(values),std::ostream_iterator<int>{std::cout," "});
std::cout<< std::endl;
这段代码的输出如下:
17 11 40 36 22 54 48 70 61 82 78 89 99 92 43
17 11 22 36 40 54 48 70 61 82 78 89 99 92 43
第一行显示的是元素原始的顺序,第二行显示的是分区之后的顺序。两次分区操作改变了元素的顺序,但改变的并不多,现在我们可以将 equal_range() 应用到 values 容器的元素上,
期望值为 wanted:
auto pr = std::equal_range(std::begin(values), std::end(values), wanted);
std::cout << "the lower bound for " << wanted << " is " << *pr.first << std::endl;
std::cout << "the upper bound for " << wanted << " is " << *pr.second << std::endl;
这段代码和前一段代码的输出是相同的,在前一段代码中,用容器对象的成员函数 sort() 对元素进行了完全排序。本节的所有算法都可以用于以这种方式分区的序列上。显然,如果分区
使用的是>,那么在查找算法中使用的函数对象也必须和它保持一致。
在前一段代码中,将 equal_range() 应用到了一个只包含单个期望值的序列中。如果这个序列包含多个实例,pr.first 会指向 wanted 的第一个匹配项,所以 [pr.first, pr.second)
这个范围包含的是所有的匹配项
[14]C++ partition(STL partition)算法使用详解
在序列中分区元素会重新对元素进行排列,所有使给定谓词返回 true 的元素会被放在所有使谓词返回 false 的元素的前面。这就是 partition() 算法所做的事。
partition 的前两个参数是定义被分区序列范围的正向迭代器,第三个参数是一个谓词。
这个谓词可以不必是用来处理顺序关系的一它可以是我们喜欢的任何样子。例如,可以对表示个体的 Person 对象进行分区,将所有女性放在男性的前面,或者将有大学学历
的放在没有大学学历的前面。下面是一个对 tuple 对象的序列进行分区的示例,这个元组对象用来表示人和标识他们的性别:
using gender = char;
using first = string;
using second= string;
using Name = std::tuple<first, second, gender>;
std:: vector<Name> names {std::make_tuple ("Dan", "old", 'm'),std::make_tuple("Ann", "old", 'f'),std::make_tuple ("Ed", "old",'m'),std::make_tuple ("Jan", "old", 'f'), std::make_tuple ("Edna", "old", 'f')};
std::partition(std::begin(names), std::end(names),[](const Names name) { return std::get<2>(name) == 'f'; });
for(const auto& name : names)
std:: cout << std::get<0> (name) << " "<< std::get<1> (name) << std::endl;
这里使用 using 声明来解释 tuple 对象成员变量的意义。当 tuple 对象的最后一个成员变量是“f”时,这个谓词会返回 true,所以输出中会出现 Edna、Ann 以及处在 Ed 和 Dan 之前的
Jan。在这个谓词中,可以用表达式 std::get<gender>(name) 来引用 tuple 的第三个成员变量。这样做是可行的,因为第三个成员是唯一的,这就允许用它的类型来识别这个成员。
partition() 算法并不保证维持这个序列原始元素的相对顺序。在上面的示例中,对于原 始序列,元素 44 和 41 在 40 的后面。但在进行这项操作之后,它们就不是那样了。为了维持元素的
相对顺序,可以使用 stable_partition() 算法。它的参数和 partition() 一样,可以用下面这些语句来代替前一段代码中的 partition() 调用:
std::stable_partition(std::begin(temperatures), std::end(temperatures),[average](double t) { return t < average; });
做出这些修改后,对应的输出如下:
65 75 56 48 31 28 32 29 40 41 44 50
Average temperature: 44.9167
31 28 32 29 40 41 44 65 75 56 48 50
[15]C++ partition_copy(STL partition_copy)算法使用详解
partition_copy() 算法以和 stable_partition() 相同的方式对序列进行分区,但那些使谓词返回 true 的元素会被复制到一个单独的序列中,使谓词返回 false 的那些元素会被复制到第三个序列中。这个操作不会改变原始序列。
原始序列由前两个参数指定,它们必须是输入迭代器。第 3 个参数用来确定目的序列的开始位置,它会保存那些使谓词返回 true 的元素。第 4 个参数用来确定另一个目的序列的开始位置,
它会保存那些使谓词返回 false 的元素。第 5 个参数是用来分区元素的谓词。下面是一个展示 partition_copy() 用法的完整程序:
// Using partition_copy() to find values above average and below average
#include <iostream> // For standard streams
#include <vector> // For vector container
#include <algorithm> // For partition_copy(), copy()
#include <numeric> // For accumulate()
#include <iterator> // For back_inserter, ostream_iterator
int main()
{
std::vector<double> temperatures {65, 75, 56, 48, 31, 28, 32, 29, 40, 41, 44, 50};
std::vector<double> low_t; // Stores below average temperatures
std::vector<double> high_t; // Stores average or above temperatures
auto average = std::accumulate(std::begin(temperatures), std::end(temperatures), 0.0) / temperatures.size();
std::partition_copy(std::begin(temperatures), std::end(temperatures), std::back_inserter(low_t), std::back_inserter(high_t),[average](double t) { return t < average; });
// Output below average temperatures
std::copy(std::begin(low_t), std::end(low_t), std::ostream_iterator<double>{std::cout, " "});
std::cout << std::endl;
// Output average or above temperatures
std::copy(std::begin(high_t), std::end(high_t), std::ostream_iterator<double>{std::cout, " "});
std::cout << std::endl;
}
这段代码所做的事情和先前介绍的 stable_partition() 相同,但小于平均值的元素会被复制到 low_t 容器中,大于等于平均值的元素会被复制到 high_t 容器中。输出语句可以对此进行验证,它们产生的输出如下:
31 28 32 29 40 41 44
65 75 56 48 50
注意,main() 中的这段代码使用辅助函数 back_inserter() 创建的 back_insert_iterator 对象作为 partition_copy() 调用中两个目的容器的迭代器。back_insert_iterator 通过调用 push_back()
向容器中添加新元素,使用这种方式可以不需要提前知道容器中保存了多少元素。如果对目的序列使用开始迭代器,在执行这个操作前,为了可以复制尽可能多的元素,目的序列中必须有足够的元素。
注意,如果输入序列和输出序列重叠,这个算法将无法正常工作。
[16]C++ partition_point(STL partition_point)算法使用详解。*获取分区序列中第一个分区的结束迭代器* + std::is_partitioned()
可以用 partition_point() 算法来获取分区序列中第一个分区的结束迭代器,它的前两个参数定义检查范围的正向迭代器,最后一个参数是用来对序列进行分区的谓词。
我们通常不知道每个分区中元素的个数,这个算法使我们能够访问和提取这些分区中的元素。例如:
std::vector<double> temperatures {65, 75, 56, 48, 31, 28, 32, 29, 40, 41, 44, 50};
auto average = std::accumulate(std::begin(temperatures),std::end(temperatures), 0.0)/ temperatures.size();
auto predicate = [average](double t) { return t < average; };
std::stable_partition(std::begin(temperatures), std::end(temperatures), predicate);
auto iter = std::partition_point(std::begin(temperatures),std::end(temperatures), predicate);
std::cout << "Elements in the first partition: ";
std::copy(std::begin(temperatures), iter,std::ostream_iterator<double>{std::cout, " "});
std::cout << "\nElements in the second partition: ";std::copy(iter, std::end(temperatures),std::ostream_iterator<double>{std::cout, " "});
std::cout << std::endl;
这段代码会相对于平均温度对 temperatures 中的元素进行分区,并通过调用 partition_point() 找到这个序列的分区点。这个分区点是第一个分区的结束迭代器,它被保存在 iter 中。
所以 [std::begin(temperatures),iter) 对应的就是第一个分区中的元素,[iter,std::end(temperatures)) 包含的是第二个分区中的元素。这里使用两次 copy() 算法来输出分区,
输出内容如下:
Elements in the first partition: 31 28 32 29 40 41 44
Elements in the second partition: 65 75 56 48 50
在使用 partition_point() 之前,需要确定序列是否已经被分区!!如果对此不是很确定,在这种情况下可以使用 is_partitioned() 来判断。它的参数是用来指定序列的输入迭代器
和用来对序列进行分区的谓词。如果这个序列已经被分区,这个算法就返回 true,否则返回 false。
在对 temperatures 使用 partition_point() 算法之前,可以先用它来验证这个序列:
if(std::is_partitioned(std::begin(temperatures), std::end(temperatures),[average](double t) { return t < average; }))
{
auto iter = std::partition_point(std::begin(temperatures),std::end(temperatures), [average](double t) { return t < average; });
std::cout << "Elements in the first partition: ";
std::copy(std::begin(temperatures), iter,std::ostream_iterator<double>{std::cout, " " });
std::cout << "\nElements in the second partition: ";
std::copy(iter, std::end(temperatures),std::ostream_iterator<double>{std::cout," "});
std::cout << std::endl;
}
else
std::cout << "Range is not partitioned." << std::endl;
只有在 is_partitioned() 返回 true 时,这段代码才会执行。如果 if 语句为 true,iter 变量会指向分区点。如果想在后面继续使用 iter,可以按如下方式在 if 语句之前定义它:
std::vector<double>::iterator iter;
[17]C++ equal(STL equal)比较算法详解
可以用和比较字符串类似的方式来比较序列。如果两个序列的长度相同,并且对应元素都相等,equal() 算法会返回 true。有 4 个版本的 equal() 算法,其中两个用 == 运算符来比较元素,
另外两个用我们提供的作为参数的函数对象来比较元素,所有指定序列的迭代器都必须至少是输入迭代器。
用 == 运算符来比较两个序列的第一个版本期望 3 个输入迭代器参数,前两个参数是第一个序列的开始和结束迭代器,第三个参数是第二个序列的开始迭代器。如果第二个序列中包含的元素少于第
一个序列,结果是未定义的,(第一个序列的元素个数决定了比较多少个对应的元素!!!)。用 == 运算符的第二个版本期望 4 个参数:第一个序列的开始和结束迭代器,第二个序列的开始和结束迭代器,
如果两个序列的长度不同,那么结果总是为 false。本节会演示这两个版本,但推荐使用接受 4 个参数的版本,因为它不会产生未定义的行为。
17.1 当用 equal() 从开始迭代器开始比较两个序列时,第二个序列用来和第一个序列比较的元素个数由第一个序列的长度决定。就算第二个序列比第一个序列的元素多,equal() 仍然会返回 true。
如果为两个序列提供了开始和结束迭代器,为了使结果为 true,序列必须是相同的长度
17.2 尽管可以用 equal() 来比较两个同种类型的容器的全部内容,但最好还是使用容器的成员函数 operator==() 来做这些事。示例中的第 6 条输出语句可以这样写:
std::cout << std::boolalpha << (words1 == words2) << " "; // false
17.3 这两个版本的 equal() 接受一个谓词作为额外的参数。这个谓词定义了元素之间的等价 比较。下面是一个说明它们用法的代码段:
std::vector<string> r1 { "three", "two", "ten"};
std::vector<string> r2 {"twelve", "ten", "twenty" };
std::cout << std::boolalpha<< std::equal (std::begin (r1) , std::end (r1) , std::begin (r2),[](const string& s1, const string& s2) { return s1[0] = s2[0]; })<< std::endl; // true
std::cout << std::boolalpha<<std::equal(std::begin(r1), std::end(r1), std::begin(r2), std::end(r2),[](const string& s1, const string& s2) { return s1[0] == s2[0]; }) << std::endl; // true
在 equal() 的第一次使用中,第二个序列是由开始迭代器指定的。谓词是一个在字符串 参数的第一个字符相等时返回 true 的 lambda 表达式。最后一条语句表明,equal() 算法可以使用两个
全范围的序列,并使用相同的谓词。
不应该用 equal() 来比较来自于无序 map 或 set 容器中的元素序列。在无序容器中,一组给定元素的顺序可能和保存在另一个无序容器中的一组相等元素不同,因为不同容器的元素很可能会被
分配到不同的格子中。
[18]C++ mismatch(STL mismatch)算法详解
equal() 算法可以告诉我们两个序列是否匹配。mismatch() 算法也可以告诉我们两个序列是否匹配,而且如果不匹配,它还能告诉我们不匹配的位置。
mismatch() 的 4 个版本和 equal() 一样有相同的参数——第二个序列有或没有结束迭代器,有或没有定义比较的额外的函数对象参数。mismatch() 返回的 pair 对象包含两个迭代器。
它的 first 成员是一个来自前两个参数所指定序列的迭代器,second 是来自于第二个序列的迭代器。当序列不匹配时,pair 包含的迭代器指向第一对不匹配的元素;因此这个 pair 对象为
pair<iter1+n,iter2 + n>,这两个序列中索引为 n 的元素是第一个不匹配的元素。
当序列匹配时,pair 的成员取决于使用的 mismatch() 的版本和具体情况。iter1 和 end_iter1 表示定义第一个序列的迭代器,iter2 和 end_iter2 表示第二个序列的开始和结束迭代器。
返回的匹配序列的 pair 的内容如下:
对于 mismatch(iter1,end_iter1,iter2):
返回 pair<end_iter1,(iter2 + (end_ter1 - iter1))>,pair 的成员 second 等于 iter2 加上第一个序列的长度。如果第二个序列比第一个序列短,结果是未定义的。
对于 mismatch(iterl, end_iter1, iter2, end_iter2):
当第一个序列比第二个序列长时,返回 pair<end_iter1, (iter2 + (end_iter1 - iter1))>,所以成员 second 为 iter2 加上第一个序列的长度。
当第二个序列比第一个序列长时,返回 pair<(iter1 + (end_iter2 - iter2)),end_iter2>, 所以成员 first 等于 iter1 加上第二个序列的长度。
当序列的长度相等时,返回 pair<end_iter1, end_iter2>。
不管是否添加一个用于比较的函数对象作为参数,上面的情况都同样适用。
文字来源:http://c.biancheng.net/view/580.html