//====================================================
function getPermutations(str){
//Enclosed data to be used by the internal recursive function permutate():
var permutations = [], //generated permutations stored here
nextWord = [], //next word builds up in here
chars = [] //collection for each recursion level
;
//---------------------
//split words or numbers into an array of characters
if (typeof str === 'string') chars = str.split('');
else if (typeof str === 'number') {
str = str + ""; //convert number to string
chars = str.split('');//convert string into char array
}
//============TWO Declaratives========
permutate(chars);
return permutations;
//===========UNDER THE HOOD===========
function permutate(chars){ //recursive: generates the permutations
if(chars.length === 0)permutations.push(nextWord.join(''));
for (var i=0; i < chars.length; i++){
chars.push(chars.shift()); //rotate the characters
nextWord.push(chars[0]); //use the first char in the array
permutate(chars.slice(1)); //Recurse: array-less-one-char
nextWord.pop(); //clear for nextWord (multiple pops)
}
}
//--------------------------------
}//==============END of getPermutations(str)=============
nextWord.pop()多次被调用? 不会置换(chars.slice(1));不要让nextWord.pop()执行,因为它会带你回到permutate函数的顶部?
此外,当chars变为空时,调用它上的切片permutate(chars.slice(1));谁再次填充角色?字符是否由nextWord.pop()填充;因为pop将值返回到permutate函数?
在chrome调试器中单步执行此代码并不清楚。
答案 0 :(得分:1)
递归调用
permutate
在循环内部,每次执行时都将它放在调用堆栈中。多次调用nextWord.pop
以完成执行堆栈上的每个递归调用。您可以使用此工具http://visualgo.net/recursion.html可视化递归。如果你有一个像Webstorm这样的工具,你可以在调试器中运行它,看看在第一次调用nextWord.pop
时堆栈中有三个permutate()调用。
答案 1 :(得分:0)
它将在premutate()
返回后执行。但我想通过查看代码就可以看出这一点。我想你的问题是如何预先进行复活?答案是查看for
循环。
因为我们每次只用一个字符来调用premutate()
。有意义的是,在某个时刻我们对premutate()
的一个调用将被调用一个空数组:
premutate([]); // happens when chars.slice(1) gives us an empty array
现在,让我们看看发生这种情况时会发生什么:
function permutate(chars){
// The following gets executed:
if(chars.length === 0)permutations.push(nextWord.join(''));
// This for loop is skipped because 0 < 0 is false
for (var i=0; i < chars.length; i++){/*...*/}
// we return because we skipped the for loop
}
现在基本案例已经返回,对premutate()
的所有其他调用也会返回:
function permutate(chars){
if(chars.length === 0)permutations.push(nextWord.join(''));
for (var i=0; i < chars.length; i++){
chars.push(chars.shift());
nextWord.push(chars[0]);
permutate(chars.slice(1)); // This have returned..
nextWord.pop(); // so execute this line
}
// and return so that other calls to us can also execute pop()
}
答案 2 :(得分:0)
chars.slice(1)
是从位置1开始的字符数组chars
,即第二个字符,因此调用permutate(chars.slice(1));
以少1个字符进行递归。
最终,chars.slice(1)
将返回零个字符,此时permutations.push(nextWord.join(''));
将被执行,for (var i=0; i < chars.length; i++)
将不执行内部循环块,因为其终止条件i < chars.length
已经存在如果i
的初始值为0
且chars.length
也为零,则为false。
因此,这个递归函数的终止条件是当前单词中的字符用完时。
当permutate
函数最终返回时,每次调用nextWord.pop()
时都会调用permutate
一次。
答案 3 :(得分:0)
调用permutate不会将您置于排列顶部。 (正如joe所提到的)它只是等待这个子调用完成然后继续执行该方法。
我认为你的递归排列可以简化:
// suppose input = "abc"
permutate(chars) {
if(chars.length === 2) return [chars, chars[0] + chars[1]};
var arr = permutate(chars.slice(1)) // returns ["bc", "cb"]
var out = []
for (var i=0; i < arr.length; i++) {
for (var j = 0; j < chars.length - 1; ++j) {
out.push(arr[i].split(0,j) + chars[0] + arr[i].split(j)) // "a" gets inserted at every possible position in each string
}
}
// result here is ["abc", "bac", "bca", "acb", "cab", "cba"]
return out
}
答案 4 :(得分:0)
function permutate(left, used, result) {
// If there are no more characters left to permute
if (0 == left.length) {
result.push(used);
}
// Iterate over all characters in the 'left' string
for (var i = 0; i < left.length; ++i) {
// Read the character we are going to work with in this iteration
var iObject = left[i];
// Create a new_left string that contains all the characters
// of the 'left' string without the one we are going to use now
var new_left = '';
for (j = 0; j < left.length; ++j) {
if (j != so.i) new_left += so.left[j];
}
// Create a new_used string that has all the characters of 'used'
// plus the one we are going to use now
var new_used = so.used + iObject;
// Call permute with new_left and new_used strings
permutate(new_left, new_used, result);
}
}
要在某些字符串上运行该函数,您需要像这样调用它:
permutate('abcd', '', []);
对于因涉及递归而无法获得此功能的人,我找到了一个页面,用于说明使用交互式动画的算法流程。
http://learntocode.guru/code/generate-permutations-recursively-string
只需点击播放并观察变量变化,而置换功能一直在调用自身。还要观察何时找到新的排列以及何时将其添加到生成的排列数组中。