解题代码
var totalNQueens = function (n) { let count = 0; //皇后可放置的总数 let isValid = (row, col, board, n) => { //所在行不用判断,每次都会下移一行 //判断同一列的数据是否包含 for (let i = 0; i < row; i++) { if (board[i][col] === 'Q') { return false } } //判断45度对角线是否包含 for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { if (board[i][j] === 'Q') { return false } } //判断135度对角线是否包含 for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; j--, i--) { if (board[i][j] === 'Q') { return false } } return true } let backTracing = (row, board) => { //走到最后一行,统计次数 if (row === n) { count++; return } for (let x = 0; x < n; x++) { //判断该位置是否可以放置 皇后 if (isValid(row, x, board, n)) { board[row][x] = 'Q'; //放置皇后 backTracing(row + 1, board); //递归 board[row][x] = '.'; //回溯,撤销处理结果 } } } backTracing(0, board) let board = [...Array(n)].map(v => v = ([...Array(n)]).fill('.')) //棋盘 return count };
2.4 - 78. 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同
解题思路
- 枚举出所有可选的数;加入选项;
- 撤销加入的选项,将选项加入结果
解题代码
const subsets = (nums) => { const res = []; const backTracing = (index, list) => { res.push(list.slice()); // 调用子递归前,加入解集 for (let i = index; i < nums.length; i++) { // 枚举出所有可选的数 list.push(nums[i]); // 选这个数 backTracing(i + 1, list); // 基于选这个数,继续递归 list.pop(); // 撤销选这个数 } }; backTracing(0, []); return res; };
2.5 - 77. 组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
提示:
1 <= n <= 20
1 <= k <= n
解题思路
- 枚举出所有可选的数;加入选项;
- 撤销加入的选项,将选项加入结果
- 剪枝条件:选项的长度满足条件;
解题代码
/** * @param {number} n * @param {number} k * @return {number[][]} */ var combine = function (n, k) { let result = []; let backTracing = (start, path) => { // 如果已经选满了的话,加入结果集中 if (path.length == k) { result.push(path.slice()); return; } // 从开始的数字到末尾的数字 for (let i = start; i <= n; i++) { // 加入选择列表中 path.push(i); // 继续深度搜索 backTracing(i + 1, path); // 撤销选择 path.pop(i); } }; backTracing(1, []); return result; };
2.6 - 081. 允许重复选择元素的组合
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
示例 1:
输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1
输出: []
示例 4:
输入: candidates = [1], target = 1
输出: [[1]]
示例 5:
输入: candidates = [1], target = 2
输出: [[1,1]]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
解题思路
- 将当前元素加入到选项里面,并将计算结果,传到选项,继续递归;
- 当选项和大于目标值时,结束这个选项,直到找到符合的选项,并将选项加入结果;
解题代码
var combinationSum = function (candidates, target) { const result = [], visited = []; backTracing(0, 0); return result; function backTracing(sum, cur) { if (target === sum) result.push(visited.slice()); if (target <= sum) return; for (let i = cur; i < candidates.length; i++) { visited.push(candidates[i]); //加入到选项里面 backTracing(sum + candidates[i], i);//选择这个选项,继续递归 visited.pop(); //插销这个选择 } } };
2.7 - 216. 组合总和 III
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解题思路
同组合1
解题代码
var combinationSum3 = function (k, n) { let ans = []; let backTracing = (start, path) => { if (path.length === k && path.reduce((acc, prev) => acc += prev) === n) { ans.push(path.slice()) return } for (let i = start; i <= 9; i++) { path.push(i) backTracing(i + 1, path) path.pop(i) } } backTracing(1, []) return ans };
2.8 - 17. 电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i] 是范围 ['2', '9'] 的一个数字。
解题思路
- 找到当前按钮对应的字母字符串
- 拼接按钮对应的字符串组合
- 选项满足长度,加入结果
解题代码
var letterCombinations = function (digits) { if(!digits.length) return [] const dic = { 2: 'abc', 3: 'def', 4: 'ghi', 5: 'jkl', 6: 'mno', 7: 'pqrs', 8: 'tuv', 9: 'wxyz', }, ans = []; let backTracing = (cur, index) => { if (index > digits.length - 1) { //选项满足长度,加入结果 ans.push(cur) return } let curDic = dic[digits[index]] //找到当前按钮对应的字母字符串 for (let prop of curDic) { backTracing(cur + prop,index+1) //拼接按钮对应的字符串组合 } } backTracing('', 0) return ans };
2.9 - 08.01. 三步问题
三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。
示例1:
输入:n = 3
输出:4
说明: 有四种走法
示例2:
输入:n = 5
输出:13
提示:
n范围在[1, 1000000]之间
解题代码(会超时)
var waysToStep = function (n) { let ans = [], map = [1, 2, 3] let backTracing = (path, sum) => { if (sum >= n) { if (sum == n) { ans++; } return } for (let i = 0; i < 3; i++) { path.push(map[i]); backTracing(path, sum + map[i]) path.pop(i) } } backTracing([], 0) return ans };
动态规划解法
/** * @param {number} n * @return {number} */ var waysToStep = function (n) { let dp =[],mod = 1000000007; dp[0]=0,dp[1]=1,dp[2]=2,dp[3]=4; for (let i = 4; i <= n; i++) { dp[i] = (dp[i - 1] + dp[i - 2] + dp[i - 3]) % mod } return dp[n] };