# 回溯算法秒杀所有排列/组合/子集问题
形式一:元素无重复不可复选 套路 形式二:元素可重复不可复选 先排序在剪枝 形式三:元素无重复可复选
# 排列
# 46. 全排列 (opens new window)
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function (nums) {
const path = []
const res = []
const selected = []
const traverse = () => {
if (path.length === nums.length) {
res.push([...path])
}
// // 可以使用的列表
for (let select of nums) {
// 已经选择的数字不需要再选了
if (selected.includes(select)) {
continue
}
// 选择一个数字
path.push(select)
selected.push(select)
traverse()
// 后序取消选择
path.pop()
selected.pop()
}
}
traverse()
return res
}
# 组合
# 77. 组合 (opens new window)
/**
* @param {number} n
* @param {number} k
* @return {number[][]}
*/
var combine = function (n, k) {
const path = []
const res = []
const traverse = (start) => {
// 组合的话就判断长度
if (path.length === k) {
res.push([...path])
}
for (let i = start; i < n; i++) {
path.push(i + 1)
traverse(i + 1)
path.pop()
}
}
traverse(0)
return res
}
# 子集
# 78. 子集 (opens new window)
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function (nums) {
const res = []
const path = [] // 记录路径
// 核心 用start来控制剩余选项
const traverse = (start) => {
res.push([...path])
for (let i = start; i < nums.length; i++) {
path.push(nums[i])
traverse(i + 1)
path.pop()
}
}
traverse(0)
return res
}
# 90. 子集 II (opens new window) (可重复不可以复选子集问题)
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsetsWithDup = function (nums) {
const new_nums = nums.sort()
const path = []
const res = []
const traverse = (start) => {
res.push([...path])
for (let i = start; i < new_nums.length; i++) {
if (i > start && new_nums[i] === new_nums[i - 1]) {
continue
}
path.push(nums[i])
traverse(i + 1)
path.pop()
}
}
traverse(0)
return res
}