使用 STL 算法查找集合中的前两个不相邻元素
Find First Two Non-Adjacent Elements in a set Using an STL Algorithm
所以我真的为此苦苦挣扎,甚至现在我对我的解决方案都不满意。
我有一个 set
至少包含 0,并且可能包含其他正数 int
。我需要在 set
.
中找到第一个正数 not
所以写一个标准的 while
-loop 来完成这个很容易。
i = foo.begin();
while (i != prev(foo.end()) && *i + 1 == *next(i)){
++i;
}
cout << "First number that cannot be formed " << *i + 1 << endl;
但是当我尝试编写循环的 STL 算法版本时,我遇到了像这个循环这样的失败:
auto i = foo.begin();
while (++i != prev(foo.end()) && *prev(i) + 1 == *i);
cout << "First number that cannot be formed " << *prev(i) + 1 << endl;
在以下情况下,这两个循环都正确地产生 3:
set<int> foo{0, 1, 2, 4};
但是在这种情况下,第二个循环错误地产生了 3 而不是 4:
set<int> foo{0, 1, 2, 3};
如何使用 STL 算法编写此代码并完成第一个循环的行为?
编辑:
看到部分答案后,我想增加难度。我真正想要的是不需要临时变量并且可以放在 cout
语句中的东西。
您遇到了边缘情况。一旦 i == location 在集合的末尾,您的 while 循环就会失败。在这种情况下,它在 i == 3 处结束。您需要让 i 越过数组的边界才能完成这项工作。
您可以将第 2 行更改为:
while (++i **<**= prev(foo.end()) && *prev(i) + 1 == *i);
通过使其 <=,一旦它超过 foo 末尾的值,我就会失败。
这里还有一些需要考虑的其他事项:
1) 集合不能保证被排序。
2) 在 set foo(0, 1, 1); 的情况下会发生什么?即重复失败的那个是正确的,但它也是一组末尾的那个?
您需要稍微复杂一点的算法来捕获所有这些情况。
你的循环的问题是你过早地停止了一个元素。这有效:
while (++i != foo.end() && *prev(i) + 1 == *i);
与第一个循环的区别是条件*prev(i) + 1 == *i)
而不是*i + 1 == *next(i)
;您检查的范围必须相应地改变。
您也可以使用 std::adjacent_find
:
auto i = std::adjacent_find(begin(s), end(s), [](int i, int j) { return i + 1 != j; });
if(i == s.end()) {
std::cout << *s.rbegin() + 1 << std::endl;
} else {
std::cout << *i + 1 << std::endl;
}
对编辑的回应:一种使其可完美内联的方法是
std::cout << std::accumulate(begin(s), end(s), 0,
[](int last, int x) {
return last + 1 == x ? x : last;
}) + 1 << '\n';
...但是这样效率比较低,因为它发现空隙时不会短路。另一种短路的方法是
std::cout << *std::mismatch(begin (s),
prev(end (s)),
next(begin(s)),
[](int lhs, int rhs) {
return lhs + 1 == rhs;
}).first + 1 << '\n';
你试过了吗adjacent_find
?
#include <algorithm>
#include <iostream>
#include <set>
int main()
{
std::set<int> foo{0, 1, 2, 4};
auto it = std::adjacent_find(begin(foo), end(foo),
[](auto e1, auto e2){ return (e2 - e1) > 1; });
// precondition: foo is not empty
if (it == end(foo)) --it;
std::cout << *it+1;
}
编辑:好的,如果你认为 Boost 足够标准,你可以这样做,这太棒了:
#include <algorithm>
#include <boost/iterator/counting_iterator.hpp>
#include <set>
#include <iostream>
int main()
{
std::set<int> foo{0, 1, 2, 4};
auto it =
std::mismatch(
begin(foo), end(foo),
boost::counting_iterator<int>(*begin(foo))
);
std::cout << *it.second;
}
编辑 2:我在阅读另一个问题时想到的另一个问题:
int i = 0;
std::find_if(begin(foo), end(foo),
[&i](int e){ return e != i++; });
std::cout << i;
这只是之前 mismatch
的另一种方式。
所以我真的为此苦苦挣扎,甚至现在我对我的解决方案都不满意。
我有一个 set
至少包含 0,并且可能包含其他正数 int
。我需要在 set
.
所以写一个标准的 while
-loop 来完成这个很容易。
i = foo.begin();
while (i != prev(foo.end()) && *i + 1 == *next(i)){
++i;
}
cout << "First number that cannot be formed " << *i + 1 << endl;
但是当我尝试编写循环的 STL 算法版本时,我遇到了像这个循环这样的失败:
auto i = foo.begin();
while (++i != prev(foo.end()) && *prev(i) + 1 == *i);
cout << "First number that cannot be formed " << *prev(i) + 1 << endl;
在以下情况下,这两个循环都正确地产生 3:
set<int> foo{0, 1, 2, 4};
但是在这种情况下,第二个循环错误地产生了 3 而不是 4:
set<int> foo{0, 1, 2, 3};
如何使用 STL 算法编写此代码并完成第一个循环的行为?
编辑:
看到部分答案后,我想增加难度。我真正想要的是不需要临时变量并且可以放在 cout
语句中的东西。
您遇到了边缘情况。一旦 i == location 在集合的末尾,您的 while 循环就会失败。在这种情况下,它在 i == 3 处结束。您需要让 i 越过数组的边界才能完成这项工作。
您可以将第 2 行更改为:
while (++i **<**= prev(foo.end()) && *prev(i) + 1 == *i);
通过使其 <=,一旦它超过 foo 末尾的值,我就会失败。
这里还有一些需要考虑的其他事项: 1) 集合不能保证被排序。 2) 在 set foo(0, 1, 1); 的情况下会发生什么?即重复失败的那个是正确的,但它也是一组末尾的那个?
您需要稍微复杂一点的算法来捕获所有这些情况。
你的循环的问题是你过早地停止了一个元素。这有效:
while (++i != foo.end() && *prev(i) + 1 == *i);
与第一个循环的区别是条件*prev(i) + 1 == *i)
而不是*i + 1 == *next(i)
;您检查的范围必须相应地改变。
您也可以使用 std::adjacent_find
:
auto i = std::adjacent_find(begin(s), end(s), [](int i, int j) { return i + 1 != j; });
if(i == s.end()) {
std::cout << *s.rbegin() + 1 << std::endl;
} else {
std::cout << *i + 1 << std::endl;
}
对编辑的回应:一种使其可完美内联的方法是
std::cout << std::accumulate(begin(s), end(s), 0,
[](int last, int x) {
return last + 1 == x ? x : last;
}) + 1 << '\n';
...但是这样效率比较低,因为它发现空隙时不会短路。另一种短路的方法是
std::cout << *std::mismatch(begin (s),
prev(end (s)),
next(begin(s)),
[](int lhs, int rhs) {
return lhs + 1 == rhs;
}).first + 1 << '\n';
你试过了吗adjacent_find
?
#include <algorithm>
#include <iostream>
#include <set>
int main()
{
std::set<int> foo{0, 1, 2, 4};
auto it = std::adjacent_find(begin(foo), end(foo),
[](auto e1, auto e2){ return (e2 - e1) > 1; });
// precondition: foo is not empty
if (it == end(foo)) --it;
std::cout << *it+1;
}
编辑:好的,如果你认为 Boost 足够标准,你可以这样做,这太棒了:
#include <algorithm>
#include <boost/iterator/counting_iterator.hpp>
#include <set>
#include <iostream>
int main()
{
std::set<int> foo{0, 1, 2, 4};
auto it =
std::mismatch(
begin(foo), end(foo),
boost::counting_iterator<int>(*begin(foo))
);
std::cout << *it.second;
}
编辑 2:我在阅读另一个问题时想到的另一个问题:
int i = 0;
std::find_if(begin(foo), end(foo),
[&i](int e){ return e != i++; });
std::cout << i;
这只是之前 mismatch
的另一种方式。