有一个带有一些符号的 小 数组,如['^','^','>','>','+','<','<'],我如何获得所有不同的排列?我知道有人问过类似的问题(并且已经有一些很好的答案),例如:
['^','^','>','>','+','<','<']
但是它们并没有呈现出 独特的 结果。如何仅一次 有效地 获得每个可能的结果?
对于小数组,可以使用引用的算法之一,将每个排列映射到字符串,然后将整个数组放入Set中以丢弃重复项。就像是:
let a = ['^','^','>','>','+','<','<']; let ps = permutations(a); // return value should be array of arrays. let qs = ps.map(p => p.join("")); let s = new Set(qs);
这对于带有< 10符号的数组应该可以正常工作。
< 10
否则,请参见此处,以获取可转换为JavaScript的多种方法。
一种流行的方法是Pandita算法,它使用继承规则按字典顺序枚举排列,仅有效地生成“唯一”排列。在此和此处对这种方法进行了简短说明。这是一个JavaScript(ES6)实现:
function swap(a, i, j) { const t = a[i]; a[i] = a[j]; a[j] = t; } function reverseSuffix(a, start) { if (start === 0) { a.reverse(); } else { let left = start; let right = a.length - 1; while (left < right) swap(a, left++, right--); } } function nextPermutation(a) { // 1. find the largest index `i` such that a[i] < a[i + 1]. // 2. find the largest `j` (> i) such that a[i] < a[j]. // 3. swap a[i] with a[j]. // 4. reverse the suffix of `a` starting at index (i + 1). // // For a more intuitive description of this algorithm, see: // https://www.nayuki.io/page/next-lexicographical-permutation-algorithm const reversedIndices = [...Array(a.length).keys()].reverse(); // Step #1; (note: `.slice(1)` maybe not necessary in JS?) const i = reversedIndices.slice(1).find(i => a[i] < a[i + 1]); if (i === undefined) { a.reverse(); return false; } // Steps #2-4 const j = reversedIndices.find(j => a[i] < a[j]); swap(a, i, j); reverseSuffix(a, i + 1); return true; } function* uniquePermutations(a) { const b = a.slice().sort(); do { yield b.slice(); } while (nextPermutation(b)); } let a = ['^','^','>','>','+','<','<']; let ps = Array.from(uniquePermutations(a)); let qs = ps.map(p => p.join("")); console.log(ps.length); console.log(new Set(qs).size);
该nextPermutation函数将数组就地转换为词典序的后继者,或者转换为词典序的最小值(如果该数组已经是词典序的最大值)。在第一种情况下,它返回true,否则返回false。这使您可以循环浏览从最小(排序)数组开始的所有排列,直到nextPermutation翻转并返回false。
nextPermutation
true
false