递归打印字符串的所有排列(Javascript)

时间:2016-10-08 00:28:03

标签: javascript string algorithm recursion permutation

我已经看过其他语言的这个问题的版本,但不是JS。

是否可以在一个函数中递归执行此操作?

我理解我需要获取字符串中的第一个元素,然后将其附加到每个解决方案中,以便对字符串的其余部分进行递归。 从逻辑上讲,我理解递归是如何进行的。我只是不明白如何将第一个字符串附加到每个递归解决方案上

var myString = "xyz";

function printPermut(inputString){
    var outputString;
    if(inputString.length === 0){
        return inputString;
    }
    if(inputString.length === 1){
        return inputString;
    }
    else{
       for(int i = 0; i<inputString.length(); i++){
           //something here like: 
           //outputString = outputString.concat(printPermut(inputString.slice(1))??
           //maybe store each unique permutation to an array or something?
       } 
    }
}

8 个答案:

答案 0 :(得分:55)

让我们编写一个函数,将字符串的所有排列作为数组返回。由于您不需要任何全局变量,因此返回排列至关重要。

function permut(string) {
    if (string.length < 2) return string; // This is our break condition

    var permutations = []; // This array will hold our permutations

    for (var i=0; i<string.length; i++) {
        var char = string[i];

        // Cause we don't want any duplicates:
        if (string.indexOf(char) != i) // if char was used already
            continue;           // skip it this time

        var remainingString = string.slice(0,i) + string.slice(i+1,string.length); //Note: you can concat Strings via '+' in JS

        for (var subPermutation of permut(remainingString))
            permutations.push(char + subPermutation)

    }
    return permutations;
}

要打印它们,之后只需遍历数组:

var myString = "xyz";
permutations = permut(myString);
for (permutation of permutations)
    print(permutation) //Use the output method of your choice

希望我能帮你解决问题。

答案 1 :(得分:40)

已经研究了排列问题以致死亡。 Heap's algorithm是一个众所周知的解决方案。这是JS中的一个版本,使用生成器:

function *permute(a, n = a.length) {
  if (n <= 1) yield a.slice();
  else for (let i = 0; i < n; i++) {
    yield *permute(a, n - 1);
    const j = n % 2 ? 0 : i;
    [a[n-1], a[j]] = [a[j], a[n-1]];
  }
}

console.log(Array.from(permute("xyz".split(''))).map(perm => perm.join('')));

permute用于获取和生成数组,而不是字符串,因此我们在调用之前将字符串拆分为字符,然后在打印出结果之前将字符粘贴回字符串中。

答案 2 :(得分:2)

使用递归函数遍历字符串

    

    function getPermutations(string) {
      var results = [];

      if (string.length === 1) 
      {
        results.push(string);
        return results;
      }

      for (var i = 0; i < string.length; i++) 
      {
        var firstChar = string[i];
        var otherChar = string.substring(0, i) + string.substring(i + 1);
        var otherPermutations = getPermutations(otherChar);
        
        for (var j = 0; j < otherPermutations.length; j++) {
          results.push(firstChar + otherPermutations[j]);
        }
      }
      return results;
    }
    
    var permutation = getPermutations('YES');
    console.log("Total permutation: "+permutation.length);
    console.log(permutation);

答案 3 :(得分:2)

问题分类::您可以将此问题视为探索问题,即,给定一组输入字符,探索可以采用哪些不同的方式排列它们。

解决方案: Backtracking算法在解决探索性问题方面表现出色,尽管它具有很高的时间复杂度。为了演示解决方案,请想象一下您将如何手动解决一小部分输入字符: [a,b,c]

以下是步骤:

  1. 采用最左边的字符。这是索引0处的字符,并与索引0处的目标右字符交换,即与自身交换。这是因为 [a,b,c] 本身就是有效的排列,因此我们希望保留它。交换字符通常需要两个指向每个字符的指针。因此,假设我们将有一个指针。
  2. 使用最左边的相同字符(在索引0处)与目标右字符在索引0 + 1 = 1处进行交换,即,将目标右指针再移动1步。这将为您提供输出: [b,a,c]
  3. 使用最左边的相同字符(索引0)与下一个下一个目标右边的字符(即索引0 + 1 + 1 = 2)进行交换。这将为您提供输出: [c,b,a]
  4. 好吧,现在我们需要停止,因为不再有目标右侧的字符要与最左侧的字符交换。因此,我们的 right 指针需要保持小于 input 中的最大索引。一次可以使 right 指针移动一步,可以使用 for 循环,该循环从 left 索引开始,以输入长度结束-1.

  5. 现在,您需要从上开始执行完全相同的步骤,但是移动左指针,使其指向下一个最左边的字符。但是,保留第2步和第3步的输入。可以想象这种情况的另一种方式是说:“嘿,我完成了最左边的字符。现在,我不想再使用它了,但我想从目前为止的结果中继续使用倒数第二的方法。

  6. 我们什么时候停止?当左指针到达输入字符串的长度-1时,因为该索引之后不再有字符。在递归算法(例如回溯)中,需要停止的情况称为基本情况。在我们的示例中,基本情况是:左=== input.length-1

这是图形可视化:

left index|                         Input String:
-------------------------------------------------------------------------------

left = 0  |                                                              in=[a, b, c]

                    (swap in[0] with in[0])                         (swap in[0] with in[1])                         (swap in[0] with in[2])
left = 1  |               in=[a, b, c]                                   in=[b, a, c]                                      in=[c, b, a]

            (swap in[1] with in[1]) (swap in[1] with in[2]) (swap in[1] with in[1])(swap in[1] with in[2])  (swap in[1] with in[1])(swap in[1] with in[2])
left = 2  | [a, b, c]                   [a, c, b]               [b, a, c]               [b, c, a]                   [c, b, a]           [c, a, b]

摘要:

  • 要将指针移至右侧,我们将使用递归增量
  • 要将 right 指针移至右侧,我们将使用 for 循环,但是我们需要始终从左侧指针开始,否则我们将探索我们拥有的东西已经探索过了。

回溯: 回溯算法的伪代码采用以下形式:

fun(input)
    if(base_case_check(input)) {
        //do final step
    } else {
        //choose
        fun(reduce(input)) //explore
        //un-choose
    }

我们的解决方案:

function permutate(string) {

  if(!string || string.length === 0)
    return new Set(['']);
  
  let left = 0;
  let result = new Set();
  permutationHelper(string, result, left);
  
  return result;
}

function permutationHelper(string, result, left) {
  
  if(left === string.length-1) {
    //base case
    result.add(string);
  } else {
    //recursive case
    for(let right=left; right < string.length; right++) {
      string = swap(string, left, right); //choose
      permutationHelper(string, result, left+1); // explore
      string = swap(string, left, right); //unchoose
    }
  }
}

function swap(string, left, right) {
  let tmpString = string.split('');
  let tmp = tmpString[left];
  tmpString[left] = tmpString[right];
  tmpString[right] = tmp;
  return tmpString.join('');
}
/* End of solution */

/* Tests */
let input = 'abc';
let result = permutate(input);
let expected = new Set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba']);
if(setsEquality(result, expected)) {
console.log('Congrats, you generated all permuations');
} else {
console.log('Sorry, not all permuations are generated');
}

function setsEquality(actualResult, expectedResult) {
  if (actualResult.size !== expectedResult.size) {
    return false;
  }
  for (let permutation of actualResult) {
    if (!expectedResult.has(permutation)) return false;
  }
  return true;
}

function assert(condition, desc) {
  if (condition) {
    console.log(`${desc} ... PASS`);
  } else {
    console.log(`${desc} ... FAIL`);
  }
}

摘要和时间复杂度:

  • 我们通过交换现有输入字符串中的字符来做出选择
  • 将左索引增加1后,我们将探索剩下的内容。这实际上意味着我们将所有后续递归的输入集减少1。因此,我们需要做的工作是: Nx(N-1)x(N-2)x(N-3)x ... x1 = N!。但是,由于我们需要一个 for 循环来探索我们拥有的输入,因此总时间复杂度将为: 0(N * N!)
  • 我们通过在修改后的输入字符串中交换回字符来恢复选择

答案 4 :(得分:0)

半休假主题:

给定字符串的

随机排列与 rndperm 一样简单:

i = document.getElementById("word");
b = document.getElementById("butt");

rndperm = (z) => {
  return z.split("").sort(() => ((Math.random() * 3) >> 0) - 1).join("")
}

function scramble() {
  i.value = rndperm(i.value);
}

var z;

function sci() {
  if (z != undefined) {
    clearInterval(z);
    b.innerText = "Scramble";
    z=undefined;
  } else {
    z = setInterval(scramble, 100);
    b.innerText = "Running...";
  }
}
<center><input id="word" value="HelloWorld"></input><button id="butt" onclick=sci()>Scramble</button></center>

答案 5 :(得分:0)

permutation=(str,prefix)=>{
   if(str.length==0){
      console.log(prefix);
   }
   else{
      for(let i=0;i<str.length;i++){
          let rem = str.substring(0,i)+str.substring(i+1);
          permutation(rem,prefix+str[i]);
       }
    }
 }
 let str="ABC";
 permutation(str,"");

答案 6 :(得分:0)

昨天我的面试官也提出了同样的问题,但是我没有得到正确的逻辑,于是我来到了 stackoverflow ,我来到了这里,但是现在我有了解决方案,想与所有人共享

const str_Permutations = (str,ar = []) => {
  str = `${str}`; // ensure type **String**
  if(ar.indexOf(str)>-1 || str.length !== (ar.strlen || str.length)) return false; // Checking if value is alreay there or(||) on recursive call string length should not be provided string 
  ar.strlen = ar.strlen || str.length; // Setting str length of provided value(string)
  ar.push(str);    // Pushing to array
  for(let i = 0; i<str.length;i++){
    str_Permutations(str[i] + str.split('').filter(v=>v!==str[i]).join(''),ar);
  }
  return Array.from(ar); // Removing *strlen* from main result and return **Result** as array
}
str_Permutations("ABC")

//Result: (6) ["ABC", "BAC", "CBA", "BCA", "ACB", "CAB"]

使用了 Array 的引用功能通过传递将值保存在同一 Array 中。我希望你明白我的意思!

答案 7 :(得分:-2)

var str = "abcdefgh";

for(let i = 0; i<str.length; i++){
    for(let j = i; j<=str.length; j++){
        if(i != j){
            var out = str.slice(i,j);
        console.log(out);
        }
        
    }
}