c语言 算法库
The other day out of curiosity, I looked into the C++ algorithm library. And found out quite a good number of cool features!
出于好奇,前几天,我研究了C ++算法库。 并发现了很多不错的功能!
This literally amazed me.
这真的使我惊讶。
Why? I mean I have mostly written C++ throughout my university life. And it was particularly because of my love-hate relationship with competitive programming.
为什么? 我的意思是我在大学期间大部分时间都在写C ++。 尤其是因为我与竞争性节目的仇恨关系。
And very unfortunately, I had never really taken advantage of this amazing library C++ has offered us.
非常不幸的是,我从未真正利用过C ++为我们提供的惊人库。
Gosh I felt so naïve!
天哪,我太天真了!
So I decided it was time to stop being naive and get to know the usefulness of C++ algorithms — at least at a higher level. And as an old man once said, sharing knowledge is power — so here I am.
因此,我认为该是时候停止天真了,至少在更高层次上了解C ++算法的有用性。 正如一位老人所说, 分享知识就是力量-所以我在这里。
Disclaimer: I have heavily used features from C++11 and beyond. If you are not quite familiar with newer editions of the language, the code snippets I have provided here might seem a bit clumsy. On the other hand, the library we discuss here is far more self-sufficient and elegant than anything I have written below. Feel free to find mistakes and point them out. And also, I could not really consider many of C++17 additions in this post, as most of its features are yet to be brought into life in GCC.
免责声明 :我大量使用了C ++ 11和更高版本的功能。 如果您不太熟悉该语言的较新版本,那么我在此处提供的代码段可能看起来有些笨拙。 另一方面,我们在这里讨论的库比我在下面写的任何东西都更加自给自足和优雅。 随时发现错误并指出错误。 而且,由于这篇文章中的大多数功能尚未在GCC中实现,因此我在这篇文章中并没有真正考虑到许多C ++ 17的新增内容。
So without further ado, let’s begin!
因此,事不宜迟,让我们开始吧!
all_of any_of none_of
all_of any_of none_of
These functions simply look for whether all
, any
or none
of the elements of a container follows some specific property defined by you. Check the example below:
这些功能只是寻找是否all
, any
或none
容器的元素遵循您定义的某些特定属性。 检查以下示例:
std::vector<int> collection = {3, 6, 12, 6, 9, 12};
// Are all numbers divisible by 3?
bool divby3 = std::all_of(begin(collection), end(collection), [](int x) {
return x % 3 == 0;
});
// divby3 equals true, because all numbers are divisible by 3
// Is any number divisible by 2?
bool divby2 = std::any_of(begin(collection), end(collection), [](int x) {
return x % 2 == 0;
});
// divby2 equals true because 6, 12 divisible by 2
// Is no number divisible by 6?
bool divby6 = std::none_of(begin(collection), end(collection), [](int x) {
return x % 6 == 0;
});
// divby6 equals false because 6, 12 divisible by 6
Notice how in the example, the specific property is passed as a lambda function.
请注意,在示例中, 特定属性是如何作为lambda函数传递的。
So all_of, any_of, none_of
look for some specific property in your collection
. These functions are pretty much self explanatory on what they are supposed to do. Along with the introduction of lambdas in C++11, they are pretty handy to use.
因此, all_of, any_of, none_of
会在您的collection
寻找一些特定的属性。 这些功能几乎可以解释它们应该做什么。 随着C ++ 11中lambda的引入,它们非常易于使用。
2. for_each
2. for_each
I have always been so accustomed to using age-old for
loop that this cute thing never crossed my sight. Basically, for_each
applies a function to a range of a container.
我一直习惯于使用古老for
循环功能,以至于这种可爱的东西从未见过我。 基本上, for_each
将函数应用于容器范围。
std::vector<int> collection = {2,4,4,1,1,3,9};
// notice that we pass x as reference!
std::for_each(begin(collection), end(collection), [] (int &x) {
x += 26;
});
If you are a JavaScript developer, the above code should ring a bell.
如果您是JavaScript开发人员,那么上面的代码应该会响起。
3. count count_if
3. count count_if
Pretty much like the functions described in the beginning, count
and count_if
both look for specific properties in your given collection of data.
就像开头所述的功能一样, count
和count_if
都在给定的数据集中寻找特定的属性。
std::vector<int> collection={1, 9, 9, 4, 2, 6};
// How many 9s are there in collection?
int nines = std::count(begin(collection), end(collection), 9);
// How many elements of the collection are even?
int evens = std::count_if(begin(collection), end(collection), [](int x) {
return x % 2 == 0;
});
// nines equals 2, evens equals 3
And a result, you receive the count that matches your given value, or has the given property that you provide in the form of a lambda function.
结果,您会收到与给定值匹配的计数 ,或者具有以lambda函数形式提供的给定属性。
4. find_if
4. find_if
Say you want to find the first element in your collection satisfying a particular property. You can use find_if
.
假设您要在集合中找到满足特定属性的第一个元素。 您可以使用find_if
。
std::vector<int> collection = {1, 2, 0, 5, 0, 3, 4};
// itr contains the iterator to the first element following the specific property
auto itr = std::find_if(begin(collection), end(collection), [](int x) {
return x % 2==0; // the property
});
Remember, as shown in the above example, you will get the iterator to the first element that matches your given property. So what if you want to find all the elements that match the property using find_if
?
请记住,如上面的示例所示,您将使迭代器到达与给定属性匹配的第一个元素 。 那么,如果要使用find_if
查找与该属性匹配的所有元素,该find_if
办?
5. generate
5. generate
This function essentially changes the values of your collection, or a range of it, based on the generator you provide. The generator is a function of the form T f();
where T
is a compatible type with our collection.
此函数本质上根据您提供的生成器更改集合值或集合值。 生成器是形式为T f();
的函数T f();
其中T
是与我们的集合兼容的类型。
std::vector<int> collection={1, 2, 0, 5, 0, 3, 4};
int counter=0;
// notice that we are capturing counter by reference
std::generate(begin(collection), end(collection), [&]() {
return counter++;
});
// collection gets replaced by values starting from 0
// modified collection = {0,1,2,3,4,5,6}
In the above example, notice that we are actually changing our collection in-place. And the generator here is the lambda function we provided.
在上面的示例中,请注意,我们实际上是在原地更改集合。 这里的生成器是我们提供的lambda函数。
6. shuffle
6. shuffle
From the standard of C++17, random_shuffle
has been removed. Now we prefer shuffle
which is more effective, given that it takes advantage of the header random
.
根据C ++ 17的标准, random_shuffle
已被删除。 现在,我们更喜欢shuffle
,因为它利用了header的random
,所以更有效。
std::vector<int> collection = {1, 2, 13, 5, 12, 3, 4};
std::random_device rd;
std::mt19937 rand_gen(rd());
std::shuffle(begin(collection), end(collection), rand_gen);
Note that we are using Mersenne Twister, a pseudo-random number generator introduced in C++11.
请注意,我们使用的是Mersenne Twister ,这是C ++ 11中引入的伪随机数生成器。
Random number generators have become far more mature in C++ with the introduction of random
library and inclusion of better methods.
随着随机数的引入, random
数生成器在C ++中变得更加成熟。 库并包含更好的方法。
7. nth_element
7. nth_element
This function is quite useful, given that it has an interesting complexity.
鉴于它具有有趣的复杂性,该功能非常有用。
Say you want to know the n-th element of your collection if it was sorted, but you do not want to sort the collection to make an O(n log(n)) operation.
假设您想知道集合中的第n个元素是否已排序,但是不想对集合进行排序以生成O(n log(n)) 操作。
What would you do?
你会怎么做?
Then nth_element
is your friend. It finds the desired element in O(n).
然后nth_element
是你的朋友。 它在O(n)中找到所需的元素。
std::vector<int> collection = {1, 2, 13, 5, 12, 3, 4};
auto median_pos = collection.begin() + collection.size() / 2;
std::nth_element(begin(collection), median_pos, end(collection));
// note that the original vector will be changed due to the operations
// done by nth_element
Interestingly, nth_element
may or may not make your collection sorted. It will just do whatever order it takes to find the n-th element. Here is an interesting discussion on StackOverflow.
有趣的是, nth_element
可能会或可能不会使您的集合排序。 它将执行发现第n个元素所需的任何顺序。 这是关于StackOverflow的有趣讨论。
And also, you can always add your own comparison function (like we added lambdas in previous examples) to make it more effective.
而且,您总是可以添加自己的比较功能(就像我们在前面的示例中添加的lambda一样),以使其更加有效。
8. equal_range
8. equal_range
So let’s say you have a sorted collection of integers. You want to find the range in which all the elements have a specific value. For example:
假设您有一个整数排序的集合。 您想找到所有元素都具有特定值的范围。 例如:
// sorted collection
std::vector<int> collection={1, 2, 5, 5, 5, 6, 9, 12};
// we are looking for a range where all elements equal to 5
auto range = std::equal_range(begin(collection), end(collection), 5);
// the required range is printed like this
std::cout << (range.first - begin(collection)) << " " <<
(range.second - begin(collection)) << std::endl;
In this code, we are looking for a range in the vector
that holds all 5
. The answer is (2~4)
.
在此代码中,我们正在寻找可容纳所有5
的vector
范围 。 答案是(2~4)
。
Of course we can use this function for our own custom property. You need to ensure that the property you have aligns with the order of the data. See this article for reference.
当然,我们可以将此函数用于我们自己的自定义属性。 您需要确保拥有的属性与数据的顺序对齐。 请参阅本文以供参考 。
Finally, lower_bound
and upper_bound
both can help you to achieve the same that you achieved using equal_range
.
最后, lower_bound
和upper_bound
都可以帮助您达到与equal_range
相同的效果。
9. merge inplace_merge
9. merge inplace_merge
Imagine you have two sorted collections (what a fun thing to imagine, right?), you want to merge them, and you also want the merged collection to remain sorted. How would you do that?
想象一下,您有两个排序的集合(想像中有一件有趣的事吧?),您想将它们合并,并且还希望合并的集合保持排序。 你会怎么做?
You can just add the second collection to the first one and sort the result again which adds an extra O(log(n)) factor. Instead of that, we can just use merge
.
您可以将第二个集合添加到第一个集合中,然后再次对结果进行排序,从而增加一个额外的O(log(n)) 因子。 取而代之的是,我们可以只使用merge
。
std::vector<int> c1 = {1, 2, 5, 5, 5, 6, 9, 12};
std::vector<int> c2 = {2, 4, 4, 5, 7, 15};
std::vector<int> result; // contains merged elements
std::merge(begin(c1), end(c1), begin(c2), end(c2), std::back_inserter(result));
// result = {1, 2, 2, 4, 4, 5, 5, 5, 5, 6, 7, 9, 12, 15}
On the other hand, do you remember when implementing merge sort, we need to merge two sides of our array? inplace_merge
can be conveniently used for that.
另一方面,您还记得实现合并排序时需要合并数组的两侧吗? inplace_merge
可以方便地用于此目的。
Look at this tiny merge sort based on the example given in cppreference:
根据cppreference中给出的示例查看这种微小的合并排序 :
void merge_sort(auto l, auto r)
{
if(r - l > 1)
{
auto mid = l+(r-l)/2;
merge_sort(l, mid);
merge_sort(mid, r);
std::inplace_merge(l, mid, r);
}
}
std::vector<int> collection = {2, 4, 4, 1, 1, 3, 9};
merge_sort(begin(collection), end(collection));
How cool is that!
多么酷啊!
10. minmax minmax_element
10. minmax minmax_element
minmax
returns the minimum and maximum of the given two values, or the given list. It returns a pair and it can also provide the functionality of your own comparison method. minmax_element
does the same for your container.
minmax
返回给定两个值或给定列表的最小值和最大值。 它返回一对,并且还可以提供您自己的比较方法的功能。 minmax_element
对您的容器执行相同的操作。
int a = 9, b = 12;
// out.first contains the minimum element, out.second is the maximum one
auto out = std::minmax(a, b);
std::vector<int> collection = {6, 5, 3, 2, 1, 4, 6, 7};
auto result = std::minmax_element(begin(collection), end(collection));
// you can also add compare function as the third argument
// (result.first - collection.begin()) is the index of the minimum element
// (result.second - collection.begin()) is the index of the maximum element
11. accumulate partial_sum
11. accumulate partial_sum
accumulate
does what it says, it accumulates values of your collection in the given range, using the initial value and a binary operation function. See for yourself:
accumulate
执行所说的操作,它会使用初始值和二进制运算函数在给定范围内累积集合的值。 你自己看:
std::vector<int> collection = {6, 5, 3, 2, 1, 4, 6, 7};
// Note that we are providing 0 as the initial value, as it should be.
// std::plus<int>() tells that the function should do sums
int sum = std::accumulate(begin(collection), end(collection), 0, std::plus<int>());
// What would happen if initial value was 0 instead of 1 in this call?
int prod = std::accumulate(begin(collection), end(collection), 1, std::multiplies<int>());
// You can also use your custom binary operation.
int custom = std::accumulate(begin(collection), end(collection), 0, [](int x, int y) {
return x+y;
});
So how is the value of custom
calculated?
那么, custom
的值如何计算?
At the beginning, accumulate takes the initial value (0) to the argument x
, the first value in the collection (6) to argument y
, does the operation, then assigns it to the accumulated value. In the second call, it passes the accumulated value to x
and the next element in the collection to y
, and thus proceeds.
在开始时,accumulate将初始值(0)赋给参数x
,将集合(6)中的第一个值赋给参数y
,然后进行运算,然后将其分配给累加值。 在第二次调用中,它将累加值传递给x
,并将集合中的下一个元素传递给y
,从而继续进行。
partial_sum
does things much like accumulate, but it also keeps the result of first n
terms in a destination container.
partial_sum
所做的事情很像累加,但它也保留前n
的结果 目标容器中的术语。
std::vector<int> collection = {6, 5, 3, 2, 1, 4, 6, 7};
std::vector<int> sums, mults;
// contains the partial sum of collection in result
std::partial_sum(begin(collection), end(collection), std::back_inserter(sums));
// contains the partial product
std::partial_sum(begin(collection), end(collection), std::back_inserter(mults), std::multiplies<int>());
And of course as you expected, you can use your own custom operation.
当然,正如您所期望的,您可以使用自己的自定义操作。
12. adjacent_difference
12. adjacent_difference
You want to find the adjacent differences in your values, you can simply use this function.
您想查找值中的相邻差异,只需使用此函数即可。
std::vector<int> collection = {6, 5, 3, 2, 1, 4, 6, 7};
std::vector<int> diffs;
std::adjacent_difference(begin(collection), end(collection), std::back_inserter(diffs));
// The first element of diffs will be same as the first element of collection
Pretty simple, right?
很简单,对吧?
But it can do much more. Look at this:
但是它可以做的更多。 看这个:
std::vector<int> fibs(10, 1);
std::adjacent_difference(begin(fibs), end(fibs) - 1, begin(fibs) + 1, std::plus<>{});
What do these two lines do? They find the first 10 Fibonacci numbers! Do you see how? ?
这两行是做什么的? 他们找到了前10个斐波那契数字! 看到了吗? ?
So that was it for today. Thanks for reading! I hope you learned something new.
今天就是这样。 谢谢阅读! 我希望你学到了一些新东西。
I would definitely like to bring some new stuff for ya’ll again in near future.
我肯定会在不久的将来再次为您带来一些新东西。
Cheers!
干杯!
c语言 算法库