You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

是否有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

火山引擎 最新活动