跳到主要内容

std::ranges::next_permutation() 算法

// (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>
  • Proj - (无)

ProjComp 模板参数在所有重载中默认类型为 std::identityranges::less

  • (1) 将范围 [first; last) 转换为下一个排列,其中所有排列的集合根据二进制比较函数对象 comp 和投影函数对象 proj 按字典序排序。返回

    • 如果存在这样的“下一个排列”,则返回 { last, true }
    • { last, false } 并将范围转换为字典序的第一个排列,如同通过
    ranges::sort(first, last, comp, proj);
  • (2)(1) 相同,但使用 r1 作为第一个源范围,r2 作为第二个源范围,如同使用 ranges::begin(r1) 作为 first1ranges::end(r1) 作为 last1ranges::begin(r2) 作为 first2,以及 ranges::end(r2) 作为 last2

本页描述的函数类实体是niebloids

参数

first1
last1

要排列的元素范围。

r

要排列的元素范围。

comp

比较函数对象,如果第一个参数小于第二个参数,则返回true

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>>

复杂度

给定 Nranges::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 {};

示例

Main.cpp
#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} }
本文源自此 CppReference 页面。它可能为了改进或编辑偏好而有所更改。点击“编辑此页面”查看本文档的所有更改。
悬停查看原始许可证。

std::ranges::next_permutation() 算法

// (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>
  • Proj - (无)

ProjComp 模板参数在所有重载中默认类型为 std::identityranges::less

  • (1) 将范围 [first; last) 转换为下一个排列,其中所有排列的集合根据二进制比较函数对象 comp 和投影函数对象 proj 按字典序排序。返回

    • 如果存在这样的“下一个排列”,则返回 { last, true }
    • { last, false } 并将范围转换为字典序的第一个排列,如同通过
    ranges::sort(first, last, comp, proj);
  • (2)(1) 相同,但使用 r1 作为第一个源范围,r2 作为第二个源范围,如同使用 ranges::begin(r1) 作为 first1ranges::end(r1) 作为 last1ranges::begin(r2) 作为 first2,以及 ranges::end(r2) 作为 last2

本页描述的函数类实体是niebloids

参数

first1
last1

要排列的元素范围。

r

要排列的元素范围。

comp

比较函数对象,如果第一个参数小于第二个参数,则返回true

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>>

复杂度

给定 Nranges::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 {};

示例

Main.cpp
#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} }
本文源自此 CppReference 页面。它可能为了改进或编辑偏好而有所更改。点击“编辑此页面”查看本文档的所有更改。
悬停查看原始许可证。