我会尝试一下。
Overview
您从两个具有全局作用域的数组开始:permArray
最终将保存所有排列数组,并且usedChars
是一个工作数组,用于通过所有递归调用构建每个单独的排列数组。重要的是要注意这些是only在创建的每个函数的范围内可访问的两个变量。所有其他变量对于它们自己的函数调用都有局部作用域。
然后是递归函数,它接受一个数组作为输入,并返回一个数组的数组,其中包含输入数组的所有可能的排列。现在,在这个特定的函数中,递归调用位于循环内。这很有趣,因为终止条件实际上比基本递归函数更复杂——当您传入空值时,递归调用终止input
数组和 for 循环会跳过下一个递归调用。
Summary
考虑一个四元素数组输入。在较高层次上,该函数将循环遍历该数组的四个元素,取出每个元素,并计算该较小的三个元素数组的排列。对于所有这三个元素排列,它将把拉出的原始元素附加到开头,并将这四个元素数组中的每一个添加到permArray
.
但是,为了找到较小的三元素数组的排列,我们取出每个元素,计算该较小的两个元素数组的排列,将取出的元素添加到每个排列的开头,然后return这三个元素数组中的每一个都位于递归调用堆栈中,因此可以将原始的第四个元素添加到开头并计为排列。
但是,为了找到较小的两个元素数组的排列,我们取出每个元素,计算一个元素的较小数组的排列,将取出的元素添加到每个排列的开头,然后return这两个元素数组中的每一个都位于递归调用堆栈中,因此可以将原始的第三个元素添加到该排列的开头并返回堆栈。
但是,为了找到较小的一个元素数组的排列,我们取出该元素并计算该空数组的排列,该空数组刚刚返回,而我们又将我们的一个元素返回到堆栈中,因此原始的第二个元素元素可以添加到该排列的开头并返回堆栈。
Details
让我们记下该函数中的一些步骤:
var permArr = [],
usedChars = [];
function permute(input) {
var i, ch;
for (i = 0; i < input.length; i++) { // loop over all elements
ch = input.splice(i, 1)[0]; //1. pull out each element in turn
usedChars.push(ch); // push this element
if (input.length == 0) { //2. if input is empty, we pushed every element
permArr.push(usedChars.slice()); // so add it as a permutation
}
permute(input); //3. compute the permutation of the smaller array
input.splice(i, 0, ch); //4. add the original element to the beginning
// making input the same size as when we started
// but in a different order
usedChars.pop(); //5. remove the element we pushed
}
return permArr //return, but this only matters in the last call
};
让我们使用数组 [4,3,2,1] 来追踪详细信息。
当它第一次传入时,我们将取出4,将其推送到usedChars
,将其从输入中删除,然后调用permute
在 [3,2,1] 上。在这次调用中,我们将把 3 压入usedChars
,将其从input
,并致电permute
关于[2,1]。然后我们将 2 推入usedChars
,将其从input, and call
permuteon [1]. Then we push 1 to
使用的字符, and remove it from
input`.
这给我们留下了四个深度调用,在步骤 (2) 中:
通道=1
输入=[]
使用的字符=[4,3,2,1]
在步骤 (2) 中,我们将把第一个排列 [4,3,2,1] 推到permArr
。然后,继续前进,因为input
现在为空,(3) 中的递归调用将简单地返回,在 (4) 中,我们将简单地将 1 添加回输入并从中删除 1usedChars
——这个调用返回。
因此,此时,我们已经开始备份递归调用并执行步骤 (4):
通道=2
输入=[1]
使用的字符=[4,3,2]
步骤(4)将执行算法的关键步骤:移动动作。它需要ch=2
并将其添加到开始 of input
并将其从中删除usedChars
。这意味着在步骤(5)之后,我们有:
通道=2
输入=[2,1]
使用的字符=[4,3]
现在看看我们在哪里。我们将 [4,3,2,1] 作为排列推送,然后备份并swapped2 和 1,现在我们将返回递归调用来构建 [4,3,1,2] 并将其添加为排列。之后,我们将退出更多元素,移动更多元素,然后返回排列并添加它们。
回到正题,执行步骤(5)后我们loop。这意味着我们将把 1 推到usedChars
并进行递归调用input=[2]
。该调用会将 2 推入usedChars
创建完整数组 [4,3,1,2] 并将其添加到permArray
.
因此,您将通过递归调用来上下循环,构建排列,退出,重建不同的排列,然后退出,直到循环完所有可能的组合。
我希望这有帮助!