必须先排序,否则会漏掉部分排列;std::next_permutation在已排序前提下原地生成全部唯一字典序排列,支持重复元素自动去重,但不适用于utf-8中文字符串。

用 std::next_permutation 生成全排列最简单可靠
只要字符串字符不重复,std::next_permutation 就是首选。它在原地修改、时间复杂度接近最优(O(n!)),且标准库保证正确性,不用自己推逻辑或担心越界。
关键前提是:必须先对字符串排序,否则会漏掉部分排列。比如 "bac" 未排序直接调用,第一次就返回 false,啥也得不到。
string s = "abc";
sort(s.begin(), s.end());
do {
cout
- 如果字符串含重复字符(如
"aab"),next_permutation仍能工作,但会自动去重——它只生成字典序严格递增的下一个排列,重复字符天然被跳过 - 别用
while (next_permutation(...))包裹初始状态,否则第一个排列(排序后)会被跳过 - 输入为空或单字符时,循环体仍会执行一次,符合直觉
手动实现 DFS 回溯要小心重复和剪枝
当需要控制生成过程(比如中途终止、记录路径深度、适配自定义交换规则),就得手写回溯。但重复字符处理很容易出错:仅靠 used[i] 标记不够,相同字符在不同位置可能被重复选。
正确做法是在每层 DFS 中,对相同字符做“同层去重”:先排序,再检查 i > 0 && s[i] == s[i-1] && !used[i-1] —— 注意是 !used[i-1],不是 used[i-1],否则会误剪合法分支。
示例片段(核心判断):
if (i > 0 && s[i] == s[i-1] && !used[i-1]) continue;
- 不排序直接比较
s[i] == s[i-1]是无效的,顺序不确定 -
used[i-1]为 false 表示前一个相同字符还没被用,说明当前是该字符首次出现在这一层,允许选;若为 true,说明上一个已选,当前是重复尝试,跳过 - 这个条件只在 for 循环内生效,不影响递归进入下一层
遇到中文或 UTF-8 字符串不能直接用 std::string + next_permutation
std::string 按字节操作,而 UTF-8 中文是一个字符占多个字节。直接排列会把汉字拆成乱码字节序列,结果既不是合法 UTF-8,也不是有意义的字符排列。
必须先解码成 Unicode 码点或至少是 UTF-32 字符串(如 std::u32string),再排列。C++20 起可用 std::text_encoding 和 std::decode_utf8,但更实际的做法是用第三方库(如 ICU 或 utf8cpp)做预处理。
- 用
std::string对"你好"调next_permutation,大概率得到类似"好你"的非法输出 - 哪怕只是统计排列数,也要按字符数(而非字节数)算,否则
"你好"被当成 4 个“元素”来排 - 命令行环境默认编码不可靠,测试时建议用宽字符或固定编码文件读入
性能敏感场景要避免构造大量临时 string 对象
每生成一个排列就 push_back 到 vector<string></string>,内存和拷贝开销很大。尤其 n ≥ 10 时,10! = 3.6M 个字符串,每个平均长度 10 字节,光存储就超 36MB,还不算分配碎片。
更高效的方式是传引用 + 回调函数,在 next_permutation 循环体内直接处理,比如写入文件、校验、计数,避免保存全部结果。
- 用
vector<char></char>替代string做底层容器可略减开销,但收益有限 - 若只需数量,直接用阶乘公式(去重后:n! / (c₁! × c₂! × …)),根本不用生成
- 多线程并行生成需注意
next_permutation不是线程安全的,得加锁或分段预计算起始状态
sort + next_permutation 就够了。真正卡住人的往往不是算法本身,而是字符编码理解偏差、重复字符的剪枝条件写反、或者没意识到全排列结果集增长有多快。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











