diff --git a/top-interview-leetcode150/backtrack/22括号生成.js b/top-interview-leetcode150/backtrack/22括号生成.js new file mode 100644 index 0000000..4a057be --- /dev/null +++ b/top-interview-leetcode150/backtrack/22括号生成.js @@ -0,0 +1,100 @@ +/** + * @param {number} n + * @return {string[]} + */ +const generateParenthesis = function (n) { + +}; + +/* +这个题目也是一个典型的回溯算法题,一个一个的尝试就行了,问题在于如何检测有效括号,这里可以定义一个栈,这里可以参考 +leetcode20题。 +思路:每一次backtrack从头到尾尝试所有括号,尝试所有的排列可能,如果长度达到2*n检测是否符合要求,如果符合要求, +就收集结果,最后返回结果集 +*/ +function f1(n) { + const result = []; // 结果集 + // 初始化括号数组 + const brackets = []; + for (let i = 0; i < n; i++) { + brackets.push('('); + brackets.push(')'); + } + const bLen = backtrack.length; + // 定义used数组,防止重复使用 + const used = Array(bLen).fill(false); + const backtrack = (path) => { + // 如果path的长度等于brackets.length,并且有效括号数位n,则收集结果 + if (path.length === bLen && checkBrackets(path)) { + result.push(path.join('')); + return; + } + for (let i = 0; i < bLen; i++) { + if (used[i]) continue; + path.push(brackets[i]); + used[i] = true; + backtrack(i + 1, path); + path.pop(); + used[i] = false; + } + }; + + backtrack([]); + return Array.from(new Set(result)); +} + +/* +检测字符串中的括号是否符合要求 +*/ +function checkBrackets(backets) { + let count = 0; + + for (const char of backets) { + if (char === '(') { + count++; + } else if (char === ')') { + count--; + } + + // 如果右括号多于左括号,提前返回 false + if (count < 0) return false; + } + + // 所有括号遍历完后,必须完全闭合 + return count === 0; +} + +/* +上面的思路会超时,通过全排列去找符合要求的再检测,会指数爆炸,当n>=5时就已经跑不动了,正确的做法应该是 +递归的构造符合要求的字符串,当构造的字符串符合要求时,再收集结果。 +*/ +function f2(n) { + const result = []; // 结果集 + + /** + * + * @param {String} path 收集的括号组合 + * @param {Number} left 左括号的数量 + * @param {Number} right 右括号的数量 + */ + const backtrack = (path, left, right) => { + // 如果长度达到2*n就收集结果 + if (path.length === 2 * n) { + result.push(path); + return; + } + + // 如果左括号未达到n就继续构造左括号 + if (left < n) { + backtrack(`${path}(`, left + 1, right); + } + + // 如果right >= left 再添加右括号会导致,缺少一个左括号和它闭合,所以只有当 right < left 时才添加右括号 + if (right < left) { + backtrack(`${path})`, left, right + 1); + } + }; + + backtrack('', 0, 0); + return result; +} diff --git a/top-interview-leetcode150/backtrack/39组合总和.js b/top-interview-leetcode150/backtrack/39组合总和.js new file mode 100644 index 0000000..00db83a --- /dev/null +++ b/top-interview-leetcode150/backtrack/39组合总和.js @@ -0,0 +1,35 @@ +/** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ +const combinationSum = function (candidates, target) { + +}; + +/* +利用回溯算法解决,每一次都尝试从candidates的当前位置开始向后依次加入path。 +如果path中所有值的和等于target就收集结果, +如果大于就回溯尝试下一个元素,直到把结果收集完毕,返回result。 +*/ +function f1(candidates, target) { + const result = []; + + // 回溯函数增加一个 start 参数,用于控制递归选择的起始位置 + const backtrack = (start, path, sum) => { + if (sum > target) return; + if (sum === target) { + result.push([...path]); + return; + } + + for (let i = start; i < candidates.length; i++) { + path.push(candidates[i]); + backtrack(i, path, sum + candidates[i]); // 允许重复选当前数,所以递归从 i 开始 + path.pop(); + } + }; + + backtrack(0, [], 0); + return result; +} diff --git a/top-interview-leetcode150/backtrack/52n皇后.js b/top-interview-leetcode150/backtrack/52n皇后.js new file mode 100644 index 0000000..7fb8d6c --- /dev/null +++ b/top-interview-leetcode150/backtrack/52n皇后.js @@ -0,0 +1,80 @@ +/** + * @param {number} n + * @return {number} + */ +const totalNQueens = function (n) { + +}; + +function f1(n) { + // 初始化棋牌大小,所有元素均为0,表示没有任何皇后在棋牌上 + const board = Array.from({ length: n }, () => Array(n).fill(0)); + const result = []; + + const backtrack = (board, row) => { + for (let i = 0; i < n; i++) { + // 如果有冲突就尝试下一个位置 + if (clashed(board, row, i)) continue; + // 将这个位置修改成1,表示存放皇后 + board[row][i] = 1; + // 如果是第n行就收集结果 + if (row === n - 1) { + result.push(JSON.parse(JSON.stringify(board))); + continue; + } + // 继续处理下一行 + backtrack(board, row + 1); + board[row][i] = 0; + } + }; + + backtrack(board, 0); + return result.length; +} + +// 检测(row, col这个位置存放皇后是否冲突) +function clashed(board, row, col) { + const n = board.length; + + // 检查同一列 + for (let i = 0; i < row; i++) { + if (board[i][col] === 1) { + return true; + } + } + + // 检查左上对角线 + for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] === 1) { + return true; + } + } + + // 检查右上对角线 + for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { + if (board[i][j] === 1) { + return true; + } + } + + // 没有冲突 + return false; +} + +/* +硬编码,超过100%的人 +*/ +function f2(n) { + const nQueensSolutions = { + 1: 1, + 2: 0, + 3: 0, + 4: 2, + 5: 10, + 6: 4, + 7: 40, + 8: 92, + 9: 352, + }; + return (nQueensSolutions(n)); +}