c++如何实现全排列_c++ next_permutation算法详解【进阶】

3次阅读

next_permutation是c++标准库中生成字典序下一个排列的函数,需先排序才能遍历全部不重复排列;它通过找降序后缀、交换、翻转实现,支持自定义比较但要求严格弱序。

c++如何实现全排列_c++ next_permutation算法详解【进阶】

next_permutation 是什么,它到底在做什么

next_permutation 是 C++ 标准库 中的一个函数,它对给定的序列(比如 vector 或数组)按字典序生成「下一个」排列。它不枚举全部,只负责从当前状态推进到字典序紧邻的下一个有效排列;如果当前已是最大排列(如降序),则返回 false 并将序列重置为最小排列(升序)。

它不是递归实现,也不分配额外空间,时间复杂度均摊 O(1),最坏 O(n),核心是「找最长后缀降序段 → 找后缀中首个大于前驱的元素 → 交换 + 翻转后缀」。

为什么直接用 next_permutation 前必须先排序

因为 next_permutation 只保证「字典序递增」,不保证从全排列集合的第一个开始。如果输入是乱序(如 {3,1,2}),调用一次 next_permutation 会跳到 {3,2,1},直接漏掉 {1,2,3}{1,3,2}{2,1,3} 等前面所有排列。

  • 正确做法:先用 sort(v.begin(), v.end()) 把容器排成升序(即字典序最小排列)
  • 然后在一个 do-while 循环里调用 next_permutation,确保覆盖全部 n! 种
  • 错误写法:while (next_permutation(...)) { ... } 会漏掉初始状态

示例:

立即学习C++免费学习笔记(深入)”;

vector v = {3, 1, 2}; sort(v.begin(), v.end()); // → {1,2,3} do {     // 处理 v } while (next_permutation(v.begin(), v.end())); // 共执行 6 次

next_permutation 对自定义类型或结构体怎么用

它依赖 operator,所以自定义类型必须提供可比较的严格弱序关系。不能只靠成员变量数量一致就认为可排列——缺少比较逻辑会导致行为未定义(可能无限循环或提前退出)。

  • 确保类/结构体重载了 bool operator
  • 或者传入自定义比较函数对象next_permutation(it1, it2, comp)
  • 注意:比较函数必须满足严格弱序,禁止用 或随机逻辑
  • 常见翻车点:结构体里有浮点字段,直接用 比较可能因精度导致排序不稳定

例如:

struct Point {     int x, y;     bool operator<(const Point& o) const {         return x != o.x ? x < o.x : y < o.y;     } };

性能陷阱:重复元素时 next_permutation 还能用吗

能用,但结果不是「去重全排列」——next_permutation 本身不判重,它把相等元素也视为不同位置的独立个体。若输入含重复值(如 {1,1,2}),它仍会生成全部 3! = 6 个排列,其中 {1,1,2}{1,1,2}(两 1 交换)被算作两个不同排列。

  • 想得到真正去重的排列?必须先 sort,再用 next_permutation,它内部已处理重复:当后缀中存在相同元素时,交换和翻转会自然跳过等价排列
  • 验证方式:对 {1,1,2} 排序后调用,实际只生成 3 个不同结果({1,1,2}, {1,2,1}, {2,1,1}
  • 但前提是容器已有序;如果乱序传入,重复逻辑失效,结果不可预测

真正容易被忽略的是:它的去重能力完全依赖于「输入已排序」+「元素可比较」这两个前提,缺一不可。

text=ZqhQzanResources