I have this :
vector<int> vec = {10, 4, 18, 7, 2, 10, 25, 30};
auto& pos25 = find(vec.cbegin(), vec.cend(), 25);
auto& pos18 = find(vec.cbegin(), vec.cend(), 18);
Now, i want to make a query to search for 7 between there two positions. I can just use operator<
between pos25
and pos18
since they're random acess iterators and then I can find
the location of 7 within that range.
But what if my container is a forward_list
. How will I implement that since I don't have an operator<
to compare these two iterators; hence I can't know whether pos25
or pos18
occur first to give a range to the find
function.
I found this method in a book :
pos18 = find (vec.begin(), vec.end(), // range
18); // value
pos25 = find (vec.begin(), pos18, // range
25); // value
if (pos18 != coll.end() && pos25 != pos18) {
// pos25 is in front of pos18
// so, only [pos25,pos18) is valid
...
}
else {
pos25 = find (pos25, vec.end(), // range
25); // value
if (pos25 != coll.end()) {
// pos18 is in front of pos25
// so, only [pos18,pos25) is valid
...
}
else {
// 18 and/or 25 not found
...
}
}
Though this is simple enough, is there anything more efficient?
Iterating over a linked list is of relatively high cost because of the potential accesses to memory that must be made. You'll want to minimize those accesses. There are a couple things you could do to that end:
find_if
to search for either 18 or 25 find_if
again for either 7 or the other bound So your code could look like this:
const auto start = find_if(cbegin(vec), cend(vec), [](const auto& i){ return i == 18 || i == 25; });
const auto target = find_if(start, cend(vec), [finish = *start == 18 ? 25 : 18](const auto& i){ return i == 7 || i == finish; });
const auto finish = *target == 7 ? find(target, cend(vec), *start == 18 ? 25 : 18) : cend(vec);
After this if finish
doesn't point to cend(vec)
then target
is a valid pointer to the 1 st 7 in the range.
Vlad from Moscow's solution cleverly avoided the need for lambdas by using find_first_of
, but it iterated over the contents of vec
more than once, making it more expensive than my algorithm. The marriage of these 2 algorithms results in an algorithm that is faster than my original while preserving the benefit of only accessing each element once:
const int a[] = { 18, 25 };
const auto start = find_first_of(cbegin(vec), cend(vec), cbegin(a), cend(a));
const int b[] = { *start == *cbegin(a) ? *crbegin(a) : *cbegin(a), 7 };
const auto target = find_first_of(start, cend(vec), cbegin(b), cend(b));
const auto finish = *target == *crbegin(b) ? find(target, cend(vec), *cbegin(b)) : cend(vec);
Again if finish
doesn't point to cend(vec)
then target
is a valid pointer to the 1 st 7 in the range.
For starters this code snippet (if to update a typo) is wrong
vector<int> vec = {10, 4, 18, 7, 2, 10, 25, 30};
auto& pos25 = find(vec.cbegin(), vec.cend(), 25);
auto& pos18 = find(vec.cbegin(), vec.cend(), 18);
You may not bind a temporary object with a non-constant reference.
As for this approach
pos18 = find (vec.begin(), vec.end(), // range
18); // value
pos25 = find (vec.begin(), pos18, // range
25); // value
then you will need to check many conditions. For example before calling
pos25 = find (vec.begin(), pos18, // range
25); // value
you should check whether pos19
is not equal to vec.end()
.
As for determining which iterator is less or greater than you can use standard function std::distance
. However it is inefficient applied to iterators of the container std::forward_list
.
A more efficient approach is to use standard algorithm std::find_first_of
instead of the algorithm std::find
to find the first iterator of the range.
Here is a demonstrative program
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> vec = { 10, 4, 18, 7, 2, 10, 25, 30 };
int a[] = { 25, 18 };
auto first = std::find_first_of(vec.cbegin(), vec.cend(),
std::begin(a), std::end(a));
auto last = vec.cend();
auto target = vec.cend();
if (first != vec.cend())
{
last = std::find(first, vec.cend(),
*first == a[0] ? a[1] : a[0]);
}
if (last != vec.cend())
{
target = std::find(first, last, 7);
}
if (target != vec.end())
{
std::cout << 7 << " is between " << *first
<< " and " << *last << std::endl;
}
}
The program output is
7 is between 18 and 25
You don't have any options to compare two ForwardIterator
's other than operator ==
. This means that you have only two ways here:
std::find
to ensure the iterators belong to a particular part of your array. This method is implemented in code you cited and in the @Jonathan's answer. For example, you can write the following code:
template<typename ForwardIterator>
bool less(ForwardIterator lhs, ForwardIterator rhs, ForwardIterator end) {
if (lhs == rhs) {
return false; // Equal
}
while (lhs++ != end) {
if (lhs == rhs) {
return true; // rhs is after lhs
}
}
return false; // lhs is after rhs
}
Please also note that this procedure assumes that both iterators belong to the same container and has linear time complexity.
Personally, I would recommend in such situation using a RandomAccessIterator
. Yes, std::list
does not provide one, but you may use std::vector
instead.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.