是否有JavaScript函数可实现元素全排列?如何高效编写该函数?
当然可以实现啦!JavaScript本身并没有内置的全排列函数,但我们可以自己编写高效的代码来生成所有不同的排列。下面我给你介绍两种常用的高效实现方法:
高效实现全排列的两种方法
方法一:递归回溯法(易理解且常用)
递归回溯是处理全排列问题最直观的思路:我们逐步固定每个位置的元素,然后对剩下的元素递归生成全排列,直到所有元素都被纳入排列中。对于像ABCD这种无重复元素的输入,这个方法能直接生成所有4! = 24种排列。
实现代码如下:
function permute(str) { const result = []; const arr = str.split(''); // 转成数组方便操作 const backtrack = (current, remaining) => { // 递归终止:剩余元素为空,当前排列完成 if (remaining.length === 0) { result.push(current.join('')); return; } for (let i = 0; i < remaining.length; i++) { // 选择当前元素加入排列 const nextCurrent = [...current, remaining[i]]; // 筛选出剩下的未使用元素 const nextRemaining = [...remaining.slice(0, i), ...remaining.slice(i + 1)]; // 递归处理剩余元素 backtrack(nextCurrent, nextRemaining); } }; backtrack([], arr); return result; } // 测试调用 console.log(permute('ABCD')); // 输出所有24种排列组合
效率说明
时间复杂度为O(n!)——这是全排列问题的理论下界,因为必须生成所有n!种排列才能满足需求。空间复杂度主要来自递归栈(深度为O(n))和存储结果的数组(O(n!))。
方法二:Heap's迭代算法(空间更高效)
如果你想避免递归调用的开销,Heap's算法是个更好的选择。它通过原地交换元素来生成所有排列,不需要额外维护剩余元素数组,空间效率更高(除存储结果外,仅需O(n)的辅助空间)。
实现代码如下:
function heapPermute(str) { const result = []; const arr = str.split(''); const n = arr.length; const c = new Array(n).fill(0); // 跟踪交换状态的辅助数组 // 先加入初始排列 result.push(arr.join('')); let i = 0; while (i < n) { if (c[i] < i) { // 根据索引奇偶性决定交换位置 const swapIdx = i % 2 === 0 ? 0 : c[i]; [arr[i], arr[swapIdx]] = [arr[swapIdx], arr[i]]; result.push(arr.join('')); c[i] += 1; i = 0; // 重置索引,重新开始遍历 } else { c[i] = 0; i += 1; } } return result; } // 测试调用 console.log(heapPermute('ABCD'));
扩展:处理含重复元素的情况
如果输入字符串包含重复字符(比如AABC),上面的方法会生成重复排列。这时候可以在递归或迭代中加入去重逻辑,比如先排序再跳过重复元素:
修改后的递归去重版本:
function permuteUnique(str) { const result = []; const arr = str.split('').sort(); // 先排序,方便识别重复元素 const backtrack = (current, remaining) => { if (remaining.length === 0) { result.push(current.join('')); return; } for (let i = 0; i < remaining.length; i++) { // 跳过已经处理过的相同元素 if (i > 0 && remaining[i] === remaining[i - 1]) continue; const nextCurrent = [...current, remaining[i]]; const nextRemaining = [...remaining.slice(0, i), ...remaining.slice(i + 1)]; backtrack(nextCurrent, nextRemaining); } }; backtrack([], arr); return result; }
内容的提问来源于stack exchange,提问作者isletoflangherhans




