std::ranges::next_permutation() 算法
- 自 C++20 起
- 简化
- 详细
// (1)
constexpr next_permutation_result<I>
next_permutation( I first, S last, Comp comp = {}, Proj proj = {} );
// (2)
constexpr next_permutation_result<ranges::borrowed_iterator_t<R>>
next_permutation( R&& r, Comp comp = {}, Proj proj = {} );
参数类型是泛型的,并具有以下约束
I
-std::bidirectional_iterator
S
-std::sentinel_for<I>
R
-std::bidirectional_range
Comp
:- (1) -
std::sortable<I, Comp, Proj>
- (2) -
std::sortable<ranges::iterator_t<R>, Comp, Proj>
- (1) -
Proj
- (无)
Proj
和 Comp
模板参数在所有重载中默认类型为 std::identity
和 ranges::less
。
// (1)
template<
std::bidirectional_iterator I,
std::sentinel_for<I> S,
class Comp = ranges::less,
class Proj = std::identity
>
requires std::sortable<I, Comp, Proj>
constexpr next_permutation_result<I>
next_permutation( I first, S last, Comp comp = {}, Proj proj = {} );
// (2)
template<
ranges::bidirectional_range R,
class Comp = ranges::less,
class Proj = std::identity
>
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr next_permutation_result<ranges::borrowed_iterator_t<R>>
next_permutation( R&& r, Comp comp = {}, Proj proj = {} );
-
(1) 将范围 [
first
;last
) 转换为下一个排列,其中所有排列的集合根据二进制比较函数对象 comp 和投影函数对象proj
按字典序排序。返回- 如果存在这样的“下一个排列”,则返回
{ last, true }
{ last, false }
并将范围转换为字典序的第一个排列,如同通过
ranges::sort(first, last, comp, proj);
- 如果存在这样的“下一个排列”,则返回
-
(2) 与 (1) 相同,但使用
r1
作为第一个源范围,r2
作为第二个源范围,如同使用ranges::begin(r1)
作为first1
,ranges::end(r1)
作为last1
,ranges::begin(r2)
作为first2
,以及ranges::end(r2)
作为last2
。
本页描述的函数类实体是niebloids。
参数
first1 last1 | 要排列的元素范围。 |
r | 要排列的元素范围。 |
comp | 比较函数对象,如果第一个参数小于第二个参数,则返回 |
proj | 要应用于元素的投影。 |
返回值
-
(1) 如果新排列在字典序上大于旧排列,则为
ranges::next_permutation_result<I>{ last, true }
。如果已达到最后一个排列且范围已重置为第一个排列,则为ranges::next_permutation_result<I>{ last, false }
。 -
(2) 与 (1) 相同,只是返回类型为
ranges::next_permutation_result<ranges::borrowed_iterator_t<R>>
。
复杂度
给定 N
为 ranges::distance(first, last)
- (1) 最多
N / 2
次交换。 - (2) 最多
ranges::distance(r)
次交换。
平均到整个排列序列,典型的实现每次调用大约使用 3
次比较和 1.5
次交换。
异常
迭代器操作或元素交换可能抛出的任何异常。
可能的实现
当迭代器类型满足 contiguous_iterator
且其值类型的交换不调用非平凡特殊成员函数或 ADL 查找的交换时,实现(例如 MSVC STL)可能会启用向量化。
next_permutation(1) 和 next_permutation(2)
struct next_permutation_fn
{
template<std::bidirectional_iterator I, std::sentinel_for<I> S,
class Comp = ranges::less, class Proj = std::identity>
requires std::sortable<I, Comp, Proj>
constexpr ranges::next_permutation_result<I>
operator()(I first, S last, Comp comp = {}, Proj proj = {}) const
{
// check that the sequence has at least two elements
if (first == last)
return {std::move(first), false};
I i_last {ranges::next(first, last)};
I i {i_last};
if (first == --i)
return {std::move(i_last), false};
// main "permutating" loop
for (;;)
{
I i1 {i};
if (std::invoke(comp, std::invoke(proj, *--i), std::invoke(proj, *i1)))
{
I j {i_last};
while (!std::invoke(comp, std::invoke(proj, *i), std::invoke(proj, *--j)))
{ }
std::iter_swap(i, j);
std::reverse(i1, i_last);
return {std::move(i_last), true};
}
// permutation "space" is exhausted
if (i == first)
{
std::reverse(first, i_last);
return {std::move(i_last), false};
}
}
}
template<ranges::bidirectional_range R, class Comp = ranges::less,
class Proj = std::identity>
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr ranges::next_permutation_result<ranges::borrowed_iterator_t<R>>
operator()(R&& r, Comp comp = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r),
std::move(comp), std::move(proj));
}
};
inline constexpr next_permutation_fn next_permutation {};
示例
#include <algorithm>
#include <array>
#include <compare>
#include <functional>
#include <iostream>
#include <string>
struct S
{
char c;
int i;
auto operator<=>(const S&) const = default;
friend std::ostream& operator<<(std::ostream& os, const S& s)
{
return os << "{'" << s.c << "', " << s.i << "}";
}
};
auto print = [](auto const& v, char term = ' ')
{
std::cout << "{ ";
for (const auto& e : v) { std::cout << e << ' '; }
std::cout << '}' << term;
};
int main()
{
std::cout << "Generate all permutations (iterators case):\n";
std::string s {"abc"};
do { print(s); } while (std::ranges::next_permutation(s.begin(), s.end()).found);
std::cout << "\n" "Generate all permutations (range case):\n";
std::array a {'a', 'b', 'c'};
do { print(a); } while (std::ranges::next_permutation(a).found);
std::cout << "\n" "Generate all permutations using comparator:\n";
using namespace std::literals;
std::array z { "█"s, "▄"s, "▁"s };
do { print(z); } while (std::ranges::next_permutation(z, std::greater()).found);
std::cout << "\n" "Generate all permutations using projection:\n";
std::array<S, 3> r { S{'A',3}, S{'B',2}, S{'C',1} };
do { print(r, '\n'); } while (std::ranges::next_permutation(r, {}, &S::c).found);
}
Generate all permutations (iterators case):
{ a b c } { a c b } { b a c } { b c a } { c a b } { c b a }
Generate all permutations (range case):
{ a b c } { a c b } { b a c } { b c a } { c a b } { c b a }
Generate all permutations using comparator:
{ █ ▄ ▁ } { █ ▁ ▄ } { ▄ █ ▁ } { ▄ ▁ █ } { ▁ █ ▄ } { ▁ ▄ █ }
Generate all permutations using projection:
{ {'A', 3} {'B', 2} {'C', 1} }
{ {'A', 3} {'C', 1} {'B', 2} }
{ {'B', 2} {'A', 3} {'C', 1} }
{ {'B', 2} {'C', 1} {'A', 3} }
{ {'C', 1} {'A', 3} {'B', 2} }
{ {'C', 1} {'B', 2} {'A', 3} }