Javascript 生成字符串排列的递归函数 //==================================================== 函数getPermutations(str){ //内部递归函数permutate()使用的封闭数据: var permutations=[],//生成的置换存储在此处 nextWord=[],//下一个单词在这里建立 chars=[]//每个递归级别的集合 ; //--------------------- //将单词或数字拆分为字符数组 如果(typeof str==='string')chars=str.split(“”); else if(typeof str=='number'){ str=str+“”;//将数字转换为字符串 chars=str.split(“”);//将字符串转换为字符数组 } //================两个声明======== 置换(chars); 返回置换; //==================发动机罩下=========== 函数置换(chars){//递归:生成置换 if(chars.length==0)置换.push(nextWord.join(“”)); 对于(变量i=0;i
nextWord.pop()如何被多次调用? 不会置换(字符片(1));不让nextWord.pop()执行,因为它将带您返回到permutate函数的顶部 另外,当chars从调用其上的slice变为空时,permutate(chars.slice(1));谁又在填充字符?是否由nextWord.pop()填充字符;因为pop正在将值返回给置换函数Javascript 生成字符串排列的递归函数 //==================================================== 函数getPermutations(str){ //内部递归函数permutate()使用的封闭数据: var permutations=[],//生成的置换存储在此处 nextWord=[],//下一个单词在这里建立 chars=[]//每个递归级别的集合 ; //--------------------- //将单词或数字拆分为字符数组 如果(typeof str==='string')chars=str.split(“”); else if(typeof str=='number'){ str=str+“”;//将数字转换为字符串 chars=str.split(“”);//将字符串转换为字符数组 } //================两个声明======== 置换(chars); 返回置换; //==================发动机罩下=========== 函数置换(chars){//递归:生成置换 if(chars.length==0)置换.push(nextWord.join(“”)); 对于(变量i=0;i,javascript,string,Javascript,String,nextWord.pop()如何被多次调用? 不会置换(字符片(1));不让nextWord.pop()执行,因为它将带您返回到permutate函数的顶部 另外,当chars从调用其上的slice变为空时,permutate(chars.slice(1));谁又在填充字符?是否由nextWord.pop()填充字符;因为pop正在将值返回给置换函数 在chrome调试器中单步执行此代码并不清楚。它将在premutate()返回后执行。但我想从代码上看这是显而易见的。我想你的问题是,premut
在chrome调试器中单步执行此代码并不清楚。它将在
premutate()返回后执行。但我想从代码上看这是显而易见的。我想你的问题是,premutate怎么能再回来?答案是查看for
循环
因为我们每次调用premutate()
时少了一个字符。在某个时刻,我们对premutate()
的一个调用将使用空数组进行调用,这是有道理的:
//====================================================
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)=============
现在,让我们看看发生这种情况时会发生什么:
premutate([]); // happens when chars.slice(1) gives us an empty array
chars.slice(1)
是从位置1开始的字符数组chars
,即第二个字符,因此调用permutate(chars.slice(1))代码>以少1个字符的方式递归
最后,chars.slice(1)
将返回零个字符,此时permutations.push(nextWord.join(“”))当i
的初始值为0
且chars.length
也为零时,代码>将被执行,并且for(var i=0;ii
将已经为假
所以这个递归函数的终止条件是当它在当前字中用完字符时
当permutate
函数最终返回时,nextWord.pop()
将在每次调用permutate
时被调用一次。递归调用permutate
位于循环内,每次执行时都将其放在调用堆栈上。多次调用nextWord.pop
,以完成堆栈上的每个递归调用。您可以使用此工具可视化递归。如果您有Webstorm之类的工具,可以在调试器中运行它,以查看在第一次调用nextWord.pop
时堆栈中有三个permutate()调用 调用permutate不会将您重置在permutate的顶部。(正如joe所提到的)它只需等待这个子调用完成,然后继续执行该方法
我认为您的递归置换可以简化:
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()
}
//假设input=“abc”
置换(chars){
if(chars.length==2)返回[chars,chars[0]+chars[1]};
var arr=permutate(chars.slice(1))//返回[“bc”,“cb”]
var out=[]
对于(变量i=0;i
对于那些因为涉及递归而无法理解这一点的人,我发现了一个页面,该页面使用交互式动画演示了算法流程
只需单击“播放”并观察变量的变化,同时排列函数不断调用自身。还可以观察何时找到新排列以及何时将其添加到结果排列数组中。您接受答案了吗?
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()
}
// 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
}
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', '', []);