# 回溯算法

关于回溯算法这里推荐阅读 C++ 总结了回溯问题类型 带你搞懂回溯算法(大量例题) (opens new window) 这篇文章。

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来换一条路再试。 — LeetCode

回溯算法是建立在 DFS 上的,DFS 是一条道跑到黑;回溯算法是结束条件后,恢复状态,回溯上一层,再次搜索。回溯算法和 DFS 的区别就是有无状态重置。

什么时候使用回溯算法呢?当满足条件或者发现不是正确路径的时候,要撤销选择回退到上一个状态,继续尝试时,需要考虑回溯算法。

# 全排列问题

题目描述:给定一个没有重复数字的序列,返回其所有可能的全排列。

示例:   
输入: [1,2,3]
输出: [
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/VvJkup
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

全排列

从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列

function permute(nums) {
  const len = nums.length, curr = [], res = [], used = {};
  function dfs(nth) {
    if (nth === len) return res.push(curr.slice())
    nums.forEach(item => {
      if (!used[item]) {
        used[item] = true
        curr.push(item)
        dfs(nth + 1)
        curr.pop()
        used[item] = false
      }
    })
  }
  dfs(0)
  return res
}

console.log(permute([1, 2, 3]))
给定任意二维数组,输出所有的排列组合项。

比如 [['A','B'], ['a','b'], [1, 2]]

输出 ['Aa1','Aa2','Ab1','Ab2','Ba1','Ba2','Bb1','Bb2']
function permutate(arr) {
    const res = [];
    function dfs(str, arr) {
        if (arr.length === 0){
            res.push(str);
            return;
        }
        const [ head, ...values ] = arr;
        for (let i = 0, len = head.length; i < len; i++) {
            dfs(str+head[i], values);
        }
    }
    dfs('',arr);
    return res;
}
/**
 * 动态规划,下一次的结果,依赖上一次的结果
 * @param {array} arr 
 */
function permutate(arr) {
    // 第一次的结果就是二维数组的第0项
	let res = arr[0].slice();

	for (let i = 1; i < arr.length; i++) {
		const pre = res.slice();
		res = [];
		pre.forEach(item => {
			arr[i].forEach(curr => {
				res.push(item + curr)
			})
		});
	}
	console.log(res)
	return res;
}

作者:pbxrt
链接:https://juejin.cn/post/6862585470375690253
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

# 组合问题

题目描述:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

示例: 输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

# 限定组合问题:及时回溯,即为“剪枝”

题目描述:给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例: 输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combinations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

# 解题模板

function xxx(入参) {
  前期的变量定义、缓存等准备工作 
  
  // 定义路径栈
  const path = []
  
  // 进入 dfs
  dfs(起点) 
  
  // 定义 dfs
  dfs(递归参数) {
    if(到达了递归边界) {
      结合题意处理边界逻辑,往往和 path 内容有关
      return   
    }
    
    // 注意这里也可能不是 for,视题意决定
    for(遍历坑位的可选值) {
      path.push(当前选中值)
      处理坑位本身的相关逻辑
      path.pop()
    }
  }
}

当一个题目设计到穷举的时候可以尝试下上面的解题模板。