std::ranges::prev_permutation() 算法
- 自 C++20 起
- 简化
- 详细
// (1)
constexpr prev_permutation_result<I>
prev_permutation( I first, S last, Comp comp = {}, Proj proj = {} );
// (2)
constexpr prev_permutation_result<ranges::borrowed_iterator_t<R>>
prev_permutation( R&& r, Comp comp = {}, Proj proj = {} );
参数类型是泛型的,并具有以下约束
I
-std::bidirectional_iterator
(双向迭代器)S
-std::sentinel_for<I>
(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 prev_permutation_result<I>
prev_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 prev_permutation_result<ranges::borrowed_iterator_t<R>>
prev_permutation( R&& r, Comp comp = {}, Proj proj = {} );
-
(1) 将范围 [
first
;last
) 转换为前一个排列,其中所有排列的集合根据二元比较函数对象 comp 和投影函数对象proj
按字典序排序。返回- 如果存在这样的“前一个排列”,则返回
{ last, true }
{ last, false }
并将范围转换为(字典序)最后一个排列,如同通过
ranges::sort(first, last, comp, proj);
ranges::reverse(first, last); - 如果存在这样的“前一个排列”,则返回
-
(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::prev_permutation_result<I>{ last, true }
。如果已达到第一个排列且范围已重置为最后一个排列,则为ranges::prev_permutation_result<I>{ last, false }
。 -
(2) 同 (1),但返回类型为
ranges::prev_permutation_result<ranges::borrowed_iterator_t<R>>
。
复杂度
给定 N
为 ranges::distance(first, last)
- (1) 最多
N / 2
次交换。 - (2) 最多
ranges::distance(r)
次交换。
平均到整个排列序列,典型的实现每次调用大约使用 3
次比较和 1.5
次交换。
异常
迭代器操作或元素交换可能抛出的任何异常。
可能的实现
实现(例如 MSVC STL)可能在迭代器类型满足 contiguous_iterator
且交换其值类型既不调用非平凡特殊成员函数也不调用 ADL 查找的 swap 时启用向量化。
prev_permutation(1) 和 prev_permutation(2)
struct prev_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::prev_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};
auto i {first};
++i;
if (i == last)
return {std::move(i), false};
auto i_last {ranges::next(first, last)};
i = i_last;
--i;
// main "permutating" loop
for (;;)
{
auto i1 {i};
--i;
if (std::invoke(comp, std::invoke(proj, *i1), std::invoke(proj, *i)))
{
auto j {i_last};
while (!std::invoke(comp, std::invoke(proj, *--j), std::invoke(proj, *i)))
;
ranges::iter_swap(i, j);
ranges::reverse(i1, last);
return {std::move(i_last), true};
}
// permutation "space" is exhausted
if (i == first)
{
ranges::reverse(first, 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::prev_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 prev_permutation_fn prev_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 {"cba"};
do print(s);
while (std::ranges::prev_permutation(s.begin(), s.end()).found);
std::cout << "\nGenerate all permutations (range case):\n";
std::array a {'c', 'b', 'a'};
do print(a);
while (std::ranges::prev_permutation(a).found);
std::cout << "\nGenerate all permutations using comparator:\n";
using namespace std::literals;
std::array z { "▁"s, "▄"s, "█"s };
do print(z);
while (std::ranges::prev_permutation(z, std::greater()).found);
std::cout << "\nGenerate all permutations using projection:\n";
std::array<S, 3> r { S{'C',1}, S{'B',2}, S{'A',3} };
do print(r, '\n');
while (std::ranges::prev_permutation(r, {}, &S::c).found);
}
Generate all permutations (iterators case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations (range case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations using comparator:
{ ▁ ▄ █ } { ▁ █ ▄ } { ▄ ▁ █ } { ▄ █ ▁ } { █ ▁ ▄ } { █ ▄ ▁ }
Generate all permutations using projection:
{ {'C', 1} {'B', 2} {'A', 3} }
{ {'C', 1} {'A', 3} {'B', 2} }
{ {'B', 2} {'C', 1} {'A', 3} }
{ {'B', 2} {'A', 3} {'C', 1} }
{ {'A', 3} {'C', 1} {'B', 2} }
{ {'A', 3} {'B', 2} {'C', 1} }