全排列的算法,在文章JavaScript全排列的六种算法中介绍了6种,这里我只记录下我已经理解的递归实现的全排列算法。
关于递归实现的全排列算法参考了文章全排列算法的JS实现。
字符串的全排列算法
下面是代码:
1 function quanpailie(str) { 2 var result=[]; 3 if(str.length<=1){ 4 return [str]; 5 } else { 6 7 var preResult = quanpailie(str.slice(1)); 8 //console.log(preResult); 9 for (var j = 0; j < preResult.length; j++) { 10 for (var k = 0; k < preResult[j].length+1; k++) { 11 var temp=preResult[j].slice(0,k)+str[0]+preResult[j].slice(k); 12 result.push(temp); 13 } 14 } 15 return result; 16 17 } 18 }
这里说一下以上代码实现全排列的过程。
- 通过递归把字符串分割,直到末尾的最后一个字符;
- 当分割到最后一个字符时,开始执行代码中的for循环部分;
- 外层循环是对preResult数组中的每一项执行内层循环的代码;
- 内层循环是将当前项字符串在源字符串中的前一个字符分别插到当前项字符串的头部、中间部分和尾部。举个例子,如图所示:
- 递归从preResult数组中只有一项且该项只有一个字符开始,然后引入另一个字符,得到有两个字符时的全排列,依次类推,最终得到整个字符串的全排列。
一维数组的全排列
数组的全排列代码 根据以上代码修改而来,具体如下:
1 function quanpailie1(arr) { 2 var result=[]; 3 if(arr.length<=1){ 4 return [arr]; 5 } else { 6 7 var preResult = quanpailie1(arr.slice(1)); 8 console.log(preResult); 9 10 for (var j = 0; j < preResult.length; j++) { 11 for (var k = 0; k < preResult[j].length+1; k++) { 12 var temp = []; 13 //下面一行代码刚开始用的是temp.push(preResult[j]),实测不行 14 //原因是对temp[0]操作会改变preResult[j],preResult[j].length随之增加,导致循环永远不会结束 15 temp[0] = preResult[j].slice(0); 16 temp[0].splice(k,0,arr[0]); 17 //alert(temp[0]); 18 result.push(temp[0]); 19 } 20 } 21 console.log(result); 22 return result; 23 24 } 25 }
数组全排列的实现与字符串全排列的原理是相同的。
这里就说一下注释部分,我怀疑push()方法推入引用类型的值时,实际上推入的是一个指针。
下面验证:
1 var a = []; 2 var obj = { 3 name: "jack", 4 age: 30 5 } 6 a.push(obj); 7 console.log(a[0].name);//jack 8 9 obj.name = "john"; 10 console.log(a[0].name);//john 11 12 a[0].name = "may"; 13 console.log(obj.name);//may
结果符合预期,怀疑成立。