需求:编写一个算法,生成一个集合的所有可能组合,不包含重复项,或者通过递归调用函数返回结果。
大多数情况下,如果不是全部,Permutations in JavaScript?提供的答案会在循环或其他函数中递归调用函数来返回结果。
循环内递归函数调用的示例
function p(a, b, res) {
var b = b || [], res = res || [], len = a.length;
if (!len)
res.push(b)
else
for (var i = 0; i < len
// recursive call to `p` here
; p(a.slice(0, i).concat(a.slice(i + 1, len)), b.concat(a[i]), res)
, i++
);
return res
}
p(["a", "b", "c"]);
当前问题试图通过线性处理依赖于先前的排列来创建给定的排列。
例如,给定一个数组
var arr = ["a", "b", "c"];
确定可能的排列总数。
for (var len = 1, i = k = arr.length; len < i ; k *= len++);
"k" 应该返回 "6",或者是数组 ["a", "b", "c"] 可能的所有排列总数。
有了确定集合的个体排列总数,可以使用 Array.prototype.slice(),Array.prototype.concat() 和 Array.prototype.reverse() 创建并填充包含所有六个排列的结果数组。
var res = new Array(new Array(k));
res[0] = arr;
res[1] = res[0].slice(0,1).concat(res[0].slice(-2).reverse());
res[2] = res[1].slice(-1).concat(res[1].slice(0,2));
res[3] = res[2].slice(0,1).concat(res[2].slice(-2).reverse());
res[4] = res[3].slice(-2).concat(res[3].slice(0,1));
res[5] = res[4].slice(0,1).concat(res[4].slice(-2).reverse());
尝试根据在计算排列和面试题中基于《C++实用算法》中发布的一种有序字典排序算法的图形显示的模式来复制结果。
如果输入集是例如
["a", "b", "c", "d", "e"]
,则可能会出现可以扩展的模式,预期将产生120种排列。下面是一个仅依赖于先前排列填充数组的示例。
// returns duplicate entries at `j`
var arr = ["a", "b", "c", "d", "e"], j = [];
var i = k = arr.length;
arr.forEach(function(a, b, array) {
if (b > 1) {
k *= b;
if (b === i -1) {
for (var q = 0;j.length < k;q++) {
if (q === 0) {
j[q] = array;
} else {
j[q] = !(q % i)
? array.slice(q % i).reverse().concat(array.slice(0, q % i))
: array.slice(q % i).concat(array.slice(0, q % i));
}
}
}
}
})
然而,我们还没有能够在上面的js中对.slice()、.concat()、.reverse()进行必要的参数调整,以便从一个排列步进到下一个;只使用前一个数组条目来确定当前排列,而不使用递归。注意到奇偶调用的平衡,并尝试使用模数%运算符和输入数组.length来调用.reverse()或不调用["a","b","c","d","e"]数组,但没有产生没有重复条目的结果。期望的结果是可以将上述模式简化为两行连续调用的持续时间,直到所有排列完成,res填充;一次调用.reverse(),一次不调用.reverse();例如,在res[0]填充后。
// odd , how to adjust `.slice()` , `.concat()` parameters
// for array of unknown `n` `.length` ?
res[i] = res[i - 1].slice(0,1).concat(res[i - 1].slice(-2).reverse());
// even
res[i] = res[1 - 1].slice(-1).concat(res[i - 1].slice(0,2));
问题:如何调整上述模式,特别是传递给 .slice()
和 .concat()
的参数或索引,以生成给定集合的所有可能排列,而无需使用对当前处理函数的递归调用?
var arr = ["a", "b", "c"];
for (var len = 1, i = k = arr.length; len < i; k *= len++);
var res = new Array(new Array(k));
res[0] = arr;
res[1] = res[0].slice(0, 1).concat(res[0].slice(-2).reverse());
res[2] = res[1].slice(-1).concat(res[1].slice(0, 2));
res[3] = res[2].slice(0, 1).concat(res[2].slice(-2).reverse());
res[4] = res[3].slice(-2).concat(res[3].slice(0, 1));
res[5] = res[4].slice(0, 1).concat(res[4].slice(-2).reverse());
console.log(res);
编辑,更新
已找到一种利用上述模式返回按字典顺序排列的置换的过程,对于长度为.length
4 的输入,使用单个for
循环。对于长度为5
的数组,未返回预期结果。
该模式基于“计算排列和面试问题”的第二张图表[0]。
希望不使用.splice()
或.sort()
来返回结果,尽管在尝试遵守每列最后一个“旋转”要求时在此处使用了这些方法。变量r
应引用下一个排列的第一个元素的index
,它确实引用了。
如果.splice()
,.sort()
的使用遵循图表中的模式,则可以包含它们的使用;但是,在下面的js
中,它们实际上并没有遵循。
我不完全确定下面的js
问题是否仅限于if (i % (total / len) === reset)
语句后面的内容,尽管这部分需要最多的时间投入;但仍然没有返回预期结果。
具体来说,现在参考图表,在旋转时,例如将2
旋转到索引0
,将1
旋转到索引2
。试图通过使用负索引r
从右向左遍历以检索应该定位在相邻“列”的index
0
处的下一个项目来实现这一点。
在下一列中,2
将被放置在index
2
,3
将被放置在index
0
。就目前为止,我已经能够掌握或调试的部分是出现错误的区域。
同样,对于[1,2,3,4]
返回预期结果,但对于[1,2,3,4,5]
则不是。
var arr = [1, 2, 3, 4];
for (var l = 1, j = total = arr.length; l < j ; total *= l++);
for (var i = 1
, reset = 0
, idx = 0
, r = 0
, len = arr.length
, res = [arr]
; i < total; i++) {
// previous permutation
var prev = res[i - 1];
// if we are at permutation `6` here, or, completion of all
// permutations beginning with `1`;
// setting next "column", place `2` at `index` 0;
// following all permutations beginning with `2`, place `3` at
// `index` `0`; with same process for `3` to `4`
if (i % (total / len) === reset) {
r = --r % -(len);
var next = prev.slice(r);
if (r === -1) {
// first implementation used for setting item at index `-1`
// to `index` 0
// would prefer to use single process for all "rotations",
// instead of splitting into `if` , `else`, though not there, yet
res[i] = [next[0]].concat(prev.slice(0, 1), prev.slice(1, len - 1)
.reverse());
} else {
// workaround for "rotation" at from `index` `r` to `index` `0`
// the chart does not actually use the previous permutation here,
// but rather, the first permutation of that particular "column";
// here, using `r` `,i`, `len`, would be
// `res[i - (i - 1) % (total / len)]`
var curr = prev.slice();
// this may be useful, to retrieve `r`,
// `prev` without item at `r` `index`
curr.splice(prev.indexOf(next[0]), 1);
// this is not optiomal
curr.sort(function(a, b) {
return arr.indexOf(a) > arr.indexOf(b)
});
// place `next[0]` at `index` `0`
// place remainder of sorted array at `index` `1` - n
curr.splice(0, 0, next[0])
res[i] = curr
}
idx = reset;
} else {
if (i % 2) {
// odd
res[i] = prev.slice(0, len - 2).concat(prev.slice(-2)
.reverse())
} else {
// even
--idx
res[i] = prev.slice(0, len - (len - 1))
.concat(prev.slice(idx), prev.slice(1, len + (idx)))
}
}
}
// try with `arr` : `[1,2,3,4,5]` to return `res` that is not correct;
// how can above `js` be adjusted to return correct results for `[1,2,3,4,5]` ?
console.log(res, res.length)
资源:
(倒计时) QuickPerm头部词典排序: (正式示例_03 〜 回文)
生成所有排列[非递归]
(试图将C ++
移植到javascript
jsfiddle http://jsfiddle.net/tvvvjf3p/)
javascript
,并在当前问题中以描述所采用的过程和方法的方式发布答案? - guest271314