回溯算法的基本思路及实例

296 阅读1分钟

在做过很多回溯算法的题,总结下思路(简称回溯算法三步曲):

  1. 确定递归函数参数和返回值;
  2. 找出递归出口;
  3. 写出单层搜索的逻辑; 或者是
  4. 「路径」:记录做出的选择。
  5. 「选择列表」:通常而言,用数组存储可以选择的操作。
  6. 「结束条件」:一般而言,就是递归的结束点,也就是搜索的结束点。 回溯算法还可以总结为一句话:

回溯法可以理解成为通过选择不同的岔路口,来寻找目的地,一个岔路口一个岔路口的去尝试找到目的地,如果走错了路的话,继续返回到上一个岔路口的另外一条路,直到找到目的地。

回溯算法大致代码思路如下:

result = []
function backtrack(路径, 选择列表) {
    if('满足结束条件') { // 这里就是对答案做更新,依据实际题目出发 
        result.push(路径)
        return 
    } else {
        for(let i = 0; i < 选择列表.length; i++) { // 对一个选择列表做相应的选择 做选择 
            backtrack(路径, 选择列表) // 既然是回溯算法,那么在一次分岔路做完选择后 
            // 需要回退我们之前做的操作 撤销选择 
        } 
    } 
}

下面是个回溯算法的例子(力扣第77道算法题)#### 77.

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

/**
 * @param {number} n
 * @param {number} k
 * @return {number[][]}
 */
var combine = function(n, k) {
    let result = []   //二维数组,即最后的结果集
    let path = []     //一维数组
    const combineHelper = (n, k, startIndex) => {  //startIndex这个入参, 每次递归都加一
        if (path.length === k) {   //递归算法出口
            result.push([...path])
            return
        }
        for (let i = startIndex; i <= n - (k - path.length) + 1; ++i) {
            path.push(i)
            combineHelper(n, k, i + 1)
            path.pop()    //为了返回到上一层
        }
    }
  combineHelper(n, k, 1)
  return result
};