feat: 添加回溯算法22,39,52

This commit is contained in:
LouisFonda 2025-07-01 21:34:25 +08:00
parent a88006e4d8
commit 762fd95901
Signed by: yigencong
GPG Key ID: 29CE877CED00E966
3 changed files with 215 additions and 0 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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));
}