feat: 添加回溯算法17,39,77,46
This commit is contained in:
parent
81d4ae749b
commit
2e1ef51465
3
backtrack/README.md
Normal file
3
backtrack/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# 回溯算法
|
||||
回溯算法就是“递归+循环+剪枝”,是一种暴力搜索的方式,适合解决组合,排列,分组,SCP(例如:n皇后,数独等),
|
||||
绘制决策树更易于我们理解收集路径和回溯的过程。
|
54
backtrack/combinations/17电话号码.js
Normal file
54
backtrack/combinations/17电话号码.js
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @param {string} digits
|
||||
* @return {string[]}
|
||||
* https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
|
||||
*/
|
||||
const letterCombinations = function (digits) {
|
||||
|
||||
};
|
||||
|
||||
function f1(digits) {
|
||||
// 特殊情况处理:如果输入是空字符串,直接返回空数组
|
||||
if (!digits) return [];
|
||||
|
||||
// 数字到字母的映射表(与手机九宫格一致),下标 0 和 1 没有对应字母
|
||||
const map = [
|
||||
[], // 0
|
||||
[], // 1
|
||||
['a', 'b', 'c'], // 2
|
||||
['d', 'e', 'f'], // 3
|
||||
['g', 'h', 'i'], // 4
|
||||
['j', 'k', 'l'], // 5
|
||||
['m', 'n', 'o'], // 6
|
||||
['p', 'q', 'r', 's'], // 7
|
||||
['t', 'u', 'v'], // 8
|
||||
['w', 'x', 'y', 'z'], // 9
|
||||
];
|
||||
|
||||
const result = []; // 存放所有可能的组合结果
|
||||
|
||||
/**
|
||||
* 回溯函数
|
||||
* @param {string[]} path 当前递归路径(字符数组)
|
||||
* @param {number} start 当前处理的是 digits 的第几个字符
|
||||
*/
|
||||
const backtrack = (path, start) => {
|
||||
// 递归终止条件:如果 path 长度等于输入数字的长度,说明已经生成一个完整组合
|
||||
if (start === digits.length) {
|
||||
result.push(path.join('')); // 把字符数组转成字符串加入结果中
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前数字对应的所有字母(注意把字符转成数字作为索引)
|
||||
for (const letter of map[+digits[start]]) {
|
||||
path.push(letter); // 做选择:添加一个字母
|
||||
backtrack(path, start + 1); // 递归处理下一个数字
|
||||
path.pop(); // 回溯:撤销上一步选择,尝试其他字母
|
||||
}
|
||||
};
|
||||
|
||||
// 从空路径、起始位置 0 开始回溯搜索
|
||||
backtrack([], 0);
|
||||
|
||||
return result;
|
||||
}
|
8
backtrack/combinations/39组合总和.js
Normal file
8
backtrack/combinations/39组合总和.js
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @param {number[]} candidates
|
||||
* @param {number} target
|
||||
* @return {number[][]}
|
||||
*/
|
||||
const combinationSum = function (candidates, target) {
|
||||
|
||||
};
|
68
backtrack/combinations/77组合.js
Normal file
68
backtrack/combinations/77组合.js
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @param {number} n
|
||||
* @param {number} k
|
||||
* @return {number[][]}
|
||||
* https://leetcode.cn/problems/combinations/
|
||||
*/
|
||||
const combine = function (n, k) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
利用回溯可以轻松的解决这个问题,定义结果esult=[]用来收集结果,定义backtrack(path, start)来收集结果和回溯
|
||||
path:用来收集路径
|
||||
start: 用来标记选择组合元素的起始位置,因为组合对顺序没有要求{1,2},{2,1}算一个组合,避免重复
|
||||
*/
|
||||
function f1(n, k) {
|
||||
const result = []; // 收集符合要求的组合
|
||||
|
||||
const backtrack = (path, start) => {
|
||||
// 如果path的长度符合要求,收集结果,当path.length === k 时
|
||||
if (path.length === k) {
|
||||
result.push([...path]);
|
||||
return; // 结束这个路径的收集
|
||||
}
|
||||
|
||||
// 从start开始选择元素放入path,进行组合
|
||||
for (let i = start; i <= n; i++) {
|
||||
// 将当前元素加入组合
|
||||
path.push(i);
|
||||
// 递归,组合新的元素
|
||||
backtrack(path, i + 1);
|
||||
// 回溯,将之前的组合路径去掉
|
||||
path.pop();
|
||||
}
|
||||
};
|
||||
backtrack([], 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
剪枝优化
|
||||
*/
|
||||
function f2(n, k) {
|
||||
const result = []; // 收集符合要求的组合
|
||||
|
||||
const backtrack = (path, start) => {
|
||||
if (path.length + (n - start + 1) < k) return; // 剪枝优化
|
||||
// 如果path的长度符合要求,收集结果,当path.length === k 时
|
||||
if (path.length === k) {
|
||||
result.push([...path]);
|
||||
return; // 结束这个路径的收集
|
||||
}
|
||||
|
||||
// 从start开始选择元素放入path,进行组合
|
||||
for (let i = start; i <= n; i++) {
|
||||
// 将当前元素加入组合
|
||||
path.push(i);
|
||||
// 递归,组合新的元素
|
||||
backtrack(path, i + 1);
|
||||
// 回溯,将之前的组合路径去掉
|
||||
path.pop();
|
||||
}
|
||||
};
|
||||
backtrack([], 1);
|
||||
|
||||
return result;
|
||||
}
|
50
top-interview-leetcode150/backtrack/46全排列.js
Normal file
50
top-interview-leetcode150/backtrack/46全排列.js
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @return {number[][]}
|
||||
* http://leetcode.cn/problems/permutations/?envType=study-plan-v2&envId=top-interview-150
|
||||
*/
|
||||
const permute = function (nums) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
利用回溯算法,递归的处理,每一次都从头开始遍历,收集每一个元素,由于是全排列,不能再结果中收集同一个元素
|
||||
多次,所以定义一个used数组来标记是否已经收集,如果收集过就跳过处理下一个元素,如果path的长度和全排列的长度
|
||||
一致,就将结果收集到result中,最后返回result
|
||||
*/
|
||||
function f2(nums) {
|
||||
/**
|
||||
*
|
||||
* @param {Number[]} nums 所有元素
|
||||
* @param {Boolean[]} used 对应位置的元素是否被使用过
|
||||
* @param {Number[]} path 全排列的结果
|
||||
*/
|
||||
const backtrack = (nums, used, path) {
|
||||
// 如果path的长度和nums的长度一致,收集结果
|
||||
if(path.length === nums.length){
|
||||
result.push(path)
|
||||
return
|
||||
}
|
||||
|
||||
// 递归处理
|
||||
for(let i = 0; i<nums.length;i++){
|
||||
// 如果当前值被使用过,直接跳过
|
||||
if(used[i]) continue
|
||||
// 将当前值加入path
|
||||
path.push(nums[i])
|
||||
used[i] = true
|
||||
backtrack(nums, used, path)
|
||||
// 下面是回溯过程,将nums[i]从path中取出,并标记为未被使用
|
||||
path.pop();
|
||||
used[i] = false
|
||||
}
|
||||
}
|
||||
|
||||
let result = []; // 收集所有全排列的结果
|
||||
const n = nums.length;
|
||||
const used = Array(n).fill(false);
|
||||
const path = []; // 收集元素形成排列
|
||||
// 调用回溯过程
|
||||
backtrack(nums, used, path)
|
||||
return result
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user