【问题标题】:Explaination regarding bakctracking/recursive approach for combinations. Question regarding behaviour in recursive call关于组合的回溯/递归方法的说明。关于递归调用中的行为的问题
【发布时间】:2021-08-16 08:08:56
【问题描述】:

nums splice 和 temp pop 操作在第一次返回条件后运行两次。为什么?我已经在 console.log 中输出了所有内容以便理解,但无法得到以下打印两次的原因::

::new after rec call:: i::  0  temp::  [ 1, 2, 3 ]  nums::  []
after rec call:: temp::  [ 1, 2 ]  nums::  [ 3 ]
outside for loop::  [ 1, 2 ]  nums::  [ 3 ]
::new after rec call:: i::  0  temp::  [ 1, 2 ]  nums::  [ 3 ]
after rec call:: temp::  [ 1 ]  nums::  [ 2, 3 ]

据我了解,由于返回条件,它可以在返回后执行一次操作,因为流程将返回到调用函数的行。 对此的解释将不胜感激。

const combination = (nums) => {
  let temp = []
  let result = []

  function backtracking(temp, nums) {
    console.log("temp:: ", temp, " nums:: ", nums);
    if (nums.length === 0) {
      result.push([...temp])
      console.log("::::: result.push::  temp:: ", temp, " nums:: ", nums);
      return
    }

    for (let i = 0; i < nums.length; i++) {
      console.log("::temp.push::  i:: ", i, " temp:: ", temp, " nums:: ", nums);
      temp.push(nums[i]);
      nums.splice(i, 1);
      console.log("before rec call:: i:: ", i, " temp:: ", temp, " nums:: ", nums);
      backtracking(temp, nums);
      console.log("::new after rec call:: i:: ", i, " temp:: ", temp, " nums:: ", nums);
      nums.splice(i, 0, temp.pop());
      console.log("after rec call:: temp:: ", temp, " nums:: ", nums);
    }
    console.log("outside for loop:: ", temp, " nums:: ", nums);
  }
  console.log("before calling bakctracking:: ", temp, " nums:: ", nums);
  backtracking(temp, nums)
  console.log(" result:: ", result);
}
combination([1, 2, 3]);

以下是所有控制台日志的输出::

before calling bakctracking::  []  nums::  [ 1, 2, 3 ]
temp::  []  nums::  [ 1, 2, 3 ]
::temp.push::  i::  0  temp::  []  nums::  [ 1, 2, 3 ]
before rec call:: i::  0  temp::  [ 1 ]  nums::  [ 2, 3 ]
temp::  [ 1 ]  nums::  [ 2, 3 ]
::temp.push::  i::  0  temp::  [ 1 ]  nums::  [ 2, 3 ]
before rec call:: i::  0  temp::  [ 1, 2 ]  nums::  [ 3 ]
temp::  [ 1, 2 ]  nums::  [ 3 ]
::temp.push::  i::  0  temp::  [ 1, 2 ]  nums::  [ 3 ]
before rec call:: i::  0  temp::  [ 1, 2, 3 ]  nums::  []
temp::  [ 1, 2, 3 ]  nums::  []
::::: result.push::  temp::  [ 1, 2, 3 ]  nums::  []
::new after rec call:: i::  0  temp::  [ 1, 2, 3 ]  nums::  []//from this log --
after rec call:: temp::  [ 1, 2 ]  nums::  [ 3 ]
outside for loop::  [ 1, 2 ]  nums::  [ 3 ]
::new after rec call:: i::  0  temp::  [ 1, 2 ]  nums::  [ 3 ]
after rec call:: temp::  [ 1 ]  nums::  [ 2, 3 ]              // --to this log
::temp.push::  i::  1  temp::  [ 1 ]  nums::  [ 2, 3 ]
before rec call:: i::  1  temp::  [ 1, 3 ]  nums::  [ 2 ]
temp::  [ 1, 3 ]  nums::  [ 2 ]
::temp.push::  i::  0  temp::  [ 1, 3 ]  nums::  [ 2 ]
before rec call:: i::  0  temp::  [ 1, 3, 2 ]  nums::  []
temp::  [ 1, 3, 2 ]  nums::  []
::::: result.push::  temp::  [ 1, 3, 2 ]  nums::  []
::new after rec call:: i::  0  temp::  [ 1, 3, 2 ]  nums::  []
after rec call:: temp::  [ 1, 3 ]  nums::  [ 2 ]
outside for loop::  [ 1, 3 ]  nums::  [ 2 ]
::new after rec call:: i::  1  temp::  [ 1, 3 ]  nums::  [ 2 ]
after rec call:: temp::  [ 1 ]  nums::  [ 2, 3 ]
outside for loop::  [ 1 ]  nums::  [ 2, 3 ]
::new after rec call:: i::  0  temp::  [ 1 ]  nums::  [ 2, 3 ]
after rec call:: temp::  []  nums::  [ 1, 2, 3 ]
::temp.push::  i::  1  temp::  []  nums::  [ 1, 2, 3 ]
before rec call:: i::  1  temp::  [ 2 ]  nums::  [ 1, 3 ]
temp::  [ 2 ]  nums::  [ 1, 3 ]
::temp.push::  i::  0  temp::  [ 2 ]  nums::  [ 1, 3 ]
before rec call:: i::  0  temp::  [ 2, 1 ]  nums::  [ 3 ]
temp::  [ 2, 1 ]  nums::  [ 3 ]
::temp.push::  i::  0  temp::  [ 2, 1 ]  nums::  [ 3 ]
before rec call:: i::  0  temp::  [ 2, 1, 3 ]  nums::  []
temp::  [ 2, 1, 3 ]  nums::  []
::::: result.push::  temp::  [ 2, 1, 3 ]  nums::  []
::new after rec call:: i::  0  temp::  [ 2, 1, 3 ]  nums::  []
after rec call:: temp::  [ 2, 1 ]  nums::  [ 3 ]
outside for loop::  [ 2, 1 ]  nums::  [ 3 ]
::new after rec call:: i::  0  temp::  [ 2, 1 ]  nums::  [ 3 ]
after rec call:: temp::  [ 2 ]  nums::  [ 1, 3 ]
::temp.push::  i::  1  temp::  [ 2 ]  nums::  [ 1, 3 ]
before rec call:: i::  1  temp::  [ 2, 3 ]  nums::  [ 1 ]
temp::  [ 2, 3 ]  nums::  [ 1 ]
::temp.push::  i::  0  temp::  [ 2, 3 ]  nums::  [ 1 ]
before rec call:: i::  0  temp::  [ 2, 3, 1 ]  nums::  []
temp::  [ 2, 3, 1 ]  nums::  []
::::: result.push::  temp::  [ 2, 3, 1 ]  nums::  []
::new after rec call:: i::  0  temp::  [ 2, 3, 1 ]  nums::  []
after rec call:: temp::  [ 2, 3 ]  nums::  [ 1 ]
outside for loop::  [ 2, 3 ]  nums::  [ 1 ]
::new after rec call:: i::  1  temp::  [ 2, 3 ]  nums::  [ 1 ]
after rec call:: temp::  [ 2 ]  nums::  [ 1, 3 ]
outside for loop::  [ 2 ]  nums::  [ 1, 3 ]
::new after rec call:: i::  1  temp::  [ 2 ]  nums::  [ 1, 3 ]
after rec call:: temp::  []  nums::  [ 1, 2, 3 ]
::temp.push::  i::  2  temp::  []  nums::  [ 1, 2, 3 ]
before rec call:: i::  2  temp::  [ 3 ]  nums::  [ 1, 2 ]
temp::  [ 3 ]  nums::  [ 1, 2 ]
::temp.push::  i::  0  temp::  [ 3 ]  nums::  [ 1, 2 ]
before rec call:: i::  0  temp::  [ 3, 1 ]  nums::  [ 2 ]
temp::  [ 3, 1 ]  nums::  [ 2 ]
::temp.push::  i::  0  temp::  [ 3, 1 ]  nums::  [ 2 ]
before rec call:: i::  0  temp::  [ 3, 1, 2 ]  nums::  []
temp::  [ 3, 1, 2 ]  nums::  []
::::: result.push::  temp::  [ 3, 1, 2 ]  nums::  []
::new after rec call:: i::  0  temp::  [ 3, 1, 2 ]  nums::  []
after rec call:: temp::  [ 3, 1 ]  nums::  [ 2 ]
outside for loop::  [ 3, 1 ]  nums::  [ 2 ]
::new after rec call:: i::  0  temp::  [ 3, 1 ]  nums::  [ 2 ]
after rec call:: temp::  [ 3 ]  nums::  [ 1, 2 ]
::temp.push::  i::  1  temp::  [ 3 ]  nums::  [ 1, 2 ]
before rec call:: i::  1  temp::  [ 3, 2 ]  nums::  [ 1 ]
temp::  [ 3, 2 ]  nums::  [ 1 ]
::temp.push::  i::  0  temp::  [ 3, 2 ]  nums::  [ 1 ]
before rec call:: i::  0  temp::  [ 3, 2, 1 ]  nums::  []
temp::  [ 3, 2, 1 ]  nums::  []
::::: result.push::  temp::  [ 3, 2, 1 ]  nums::  []
::new after rec call:: i::  0  temp::  [ 3, 2, 1 ]  nums::  []
after rec call:: temp::  [ 3, 2 ]  nums::  [ 1 ]
outside for loop::  [ 3, 2 ]  nums::  [ 1 ]
::new after rec call:: i::  1  temp::  [ 3, 2 ]  nums::  [ 1 ]
after rec call:: temp::  [ 3 ]  nums::  [ 1, 2 ]
outside for loop::  [ 3 ]  nums::  [ 1, 2 ]
::new after rec call:: i::  2  temp::  [ 3 ]  nums::  [ 1, 2 ]
after rec call:: temp::  []  nums::  [ 1, 2, 3 ]
outside for loop::  []  nums::  [ 1, 2, 3 ]
result::  [
[ 1, 2, 3 ],
[ 1, 3, 2 ],
[ 2, 1, 3 ],
[ 2, 3, 1 ],
[ 3, 1, 2 ],
[ 3, 2, 1 ]
]

【问题讨论】:

    标签: javascript recursion combinations backtracking


    【解决方案1】:

    这不是一个答案,而只是一种让您更轻松地查看您正在做的事情的更清晰日志、跟踪日志语句中的递归深度并简化记录的数据的方法。

    这个输出是不是更清楚了?

    before calling bakctracking - {"temp":[],"nums":[1,2,3]}
         entering backtracking - {"temp":[],"nums":[1,2,3],"depth":1}
         temp.push - {"i":0,"temp":[],"nums":[1,2,3]}
         before rec call - {"i":0,"temp":[1],"nums":[2,3]}
             entering backtracking - {"temp":[1],"nums":[2,3],"depth":2}
             temp.push - {"i":0,"temp":[1],"nums":[2,3]}
             before rec call - {"i":0,"temp":[1,2],"nums":[3]}
                 entering backtracking - {"temp":[1,2],"nums":[3],"depth":3}
                 temp.push - {"i":0,"temp":[1,2],"nums":[3]}
                 before rec call - {"i":0,"temp":[1,2,3],"nums":[]}
                     entering backtracking - {"temp":[1,2,3],"nums":[],"depth":4}
                     result.push - {"temp":[1,2,3],"nums":[]}
                 new after rec call - {"i":0,"temp":[1,2,3],"nums":[]}
                 after rec call - {"temp":[1,2],"nums":[3]}
                 outside for loop - {"temp":[1,2],"nums":[3]}
             new after rec call - {"i":0,"temp":[1,2],"nums":[3]}
             after rec call - {"temp":[1],"nums":[2,3]}
             temp.push - {"i":1,"temp":[1],"nums":[2,3]}
             before rec call - {"i":1,"temp":[1,3],"nums":[2]}
                 entering backtracking - {"temp":[1,3],"nums":[2],"depth":3}
                 temp.push - {"i":0,"temp":[1,3],"nums":[2]}
                 before rec call - {"i":0,"temp":[1,3,2],"nums":[]}
                     entering backtracking - {"temp":[1,3,2],"nums":[],"depth":4}
                     result.push - {"temp":[1,3,2],"nums":[]}
                 new after rec call - {"i":0,"temp":[1,3,2],"nums":[]}
                 after rec call - {"temp":[1,3],"nums":[2]}
                 outside for loop - {"temp":[1,3],"nums":[2]}
             new after rec call - {"i":1,"temp":[1,3],"nums":[2]}
             after rec call - {"temp":[1],"nums":[2,3]}
             outside for loop - {"temp":[1],"nums":[2,3]}
         new after rec call - {"i":0,"temp":[1],"nums":[2,3]}
         after rec call - {"temp":[],"nums":[1,2,3]}
         temp.push - {"i":1,"temp":[],"nums":[1,2,3]}
         before rec call - {"i":1,"temp":[2],"nums":[1,3]}
             entering backtracking - {"temp":[2],"nums":[1,3],"depth":2}
             temp.push - {"i":0,"temp":[2],"nums":[1,3]}
             before rec call - {"i":0,"temp":[2,1],"nums":[3]}
                 entering backtracking - {"temp":[2,1],"nums":[3],"depth":3}
                 temp.push - {"i":0,"temp":[2,1],"nums":[3]}
                 before rec call - {"i":0,"temp":[2,1,3],"nums":[]}
                     entering backtracking - {"temp":[2,1,3],"nums":[],"depth":4}
                     result.push - {"temp":[2,1,3],"nums":[]}
                 new after rec call - {"i":0,"temp":[2,1,3],"nums":[]}
                 after rec call - {"temp":[2,1],"nums":[3]}
                 outside for loop - {"temp":[2,1],"nums":[3]}
             new after rec call - {"i":0,"temp":[2,1],"nums":[3]}
             after rec call - {"temp":[2],"nums":[1,3]}
             temp.push - {"i":1,"temp":[2],"nums":[1,3]}
             before rec call - {"i":1,"temp":[2,3],"nums":[1]}
                 entering backtracking - {"temp":[2,3],"nums":[1],"depth":3}
                 temp.push - {"i":0,"temp":[2,3],"nums":[1]}
                 before rec call - {"i":0,"temp":[2,3,1],"nums":[]}
                     entering backtracking - {"temp":[2,3,1],"nums":[],"depth":4}
                     result.push - {"temp":[2,3,1],"nums":[]}
                 new after rec call - {"i":0,"temp":[2,3,1],"nums":[]}
                 after rec call - {"temp":[2,3],"nums":[1]}
                 outside for loop - {"temp":[2,3],"nums":[1]}
             new after rec call - {"i":1,"temp":[2,3],"nums":[1]}
             after rec call - {"temp":[2],"nums":[1,3]}
             outside for loop - {"temp":[2],"nums":[1,3]}
         new after rec call - {"i":1,"temp":[2],"nums":[1,3]}
         after rec call - {"temp":[],"nums":[1,2,3]}
         temp.push - {"i":2,"temp":[],"nums":[1,2,3]}
         before rec call - {"i":2,"temp":[3],"nums":[1,2]}
             entering backtracking - {"temp":[3],"nums":[1,2],"depth":2}
             temp.push - {"i":0,"temp":[3],"nums":[1,2]}
             before rec call - {"i":0,"temp":[3,1],"nums":[2]}
                 entering backtracking - {"temp":[3,1],"nums":[2],"depth":3}
                 temp.push - {"i":0,"temp":[3,1],"nums":[2]}
                 before rec call - {"i":0,"temp":[3,1,2],"nums":[]}
                     entering backtracking - {"temp":[3,1,2],"nums":[],"depth":4}
                     result.push - {"temp":[3,1,2],"nums":[]}
                 new after rec call - {"i":0,"temp":[3,1,2],"nums":[]}
                 after rec call - {"temp":[3,1],"nums":[2]}
                 outside for loop - {"temp":[3,1],"nums":[2]}
             new after rec call - {"i":0,"temp":[3,1],"nums":[2]}
             after rec call - {"temp":[3],"nums":[1,2]}
             temp.push - {"i":1,"temp":[3],"nums":[1,2]}
             before rec call - {"i":1,"temp":[3,2],"nums":[1]}
                 entering backtracking - {"temp":[3,2],"nums":[1],"depth":3}
                 temp.push - {"i":0,"temp":[3,2],"nums":[1]}
                 before rec call - {"i":0,"temp":[3,2,1],"nums":[]}
                     entering backtracking - {"temp":[3,2,1],"nums":[],"depth":4}
                     result.push - {"temp":[3,2,1],"nums":[]}
                 new after rec call - {"i":0,"temp":[3,2,1],"nums":[]}
                 after rec call - {"temp":[3,2],"nums":[1]}
                 outside for loop - {"temp":[3,2],"nums":[1]}
             new after rec call - {"i":1,"temp":[3,2],"nums":[1]}
             after rec call - {"temp":[3],"nums":[1,2]}
             outside for loop - {"temp":[3],"nums":[1,2]}
         new after rec call - {"i":2,"temp":[3],"nums":[1,2]}
         after rec call - {"temp":[],"nums":[1,2,3]}
         outside for loop - {"temp":[],"nums":[1,2,3]}
     result - [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
    

    您可以通过扩展此 sn-p 来查看我对您的日志记录所做的细微调整:

    const {log, display} = ((lines = []) => ({
      log: (depth) => (msg, obj) => lines .push (`${'    ' .repeat (depth)} ${msg} - ${JSON .stringify (obj)}`),
      display: () => console .log (lines .join('\n'))
    }))() 
    
    const combination = (nums) => {
      let temp = []
      let result = []
    
      function backtracking(temp, nums, depth) {
        log (depth) ("entering backtracking", {temp, nums, depth});
        if (nums.length === 0) {
          result.push([...temp])
          log (depth) ("result.push", {temp, nums});
          return
        }
    
        for (let i = 0; i < nums.length; i++) {
          log (depth) ("temp.push", {i, temp, nums});
          temp.push(nums[i]);
          nums.splice(i, 1);
          log (depth) ("before rec call", {i, temp, nums});
          backtracking(temp, nums, depth + 1);
          log (depth) ("new after rec call", {i, temp, nums});
          nums.splice(i, 0, temp.pop());
          log (depth) ("after rec call", {temp, nums});
        }
        log (depth) ("outside for loop", {temp, nums});
      }
      log (0) ("before calling bakctracking", {temp, nums});
      backtracking(temp, nums, 1)
      log (0) ("result", result);
    }
    
    combination ([1, 2, 3]);
    
    display ()
    .as-console-wrapper {max-height: 100% !important; top: 0}

    【讨论】:

    • 谢谢。但是当nums长度变为零时,将临时数组添加到结果后,我仍然无法理解,当流返回回溯函数调用时,临时数组上的弹出操作完成。然后在 for 循环中调用回溯方法后,流程再次进入行。但我不明白为什么?在 num 数组的长度变为零后, temp 上的 pop 操作发生了两次,我不清楚为什么会发生这种情况。如果你对此有澄清,那么肯定会有所帮助。非常感谢
    • 我没有时间尝试理解这一点。我会发现使用不可变结构更容易理解。您是否为案例[1, 2] 尝试过调试器?应该小到足以理解并能够逐步完成。
    最近更新 更多