1、定义
回溯算法:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
解决一个回溯问题,实际上就是一个决策树的遍历过程。
2、模板
三个步骤:
(1)开头写好跳出条件,满足条件才将当前结果加入总结果中。
(2)已经拿过的数不再拿。
(3)向下遍历,结束后回溯到上一步。
伪代码:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件: // (1)
result.add(路径)
return
for 选择 in 选择列表:
去掉已经拿过的数 // (2)
做选择 // (3)
backtrack(路径, 选择列表)
撤销选择
3、leetcode真题(全排列、子集、组合、拓展题)
-
全排列问题(leetcode46)
var allArray = function(nums) {
var res = []
var dfs = function(path){
if(nums.length === path.length){ //(1)
res.push([...path]);
return;
}
for(let i=0; i<nums.length; i++){
if(path.indexOf(nums[i]) !== -1) { //(2)
continue;
}
path.push(nums[i]); //(3)
dfs(path);
path.pop();
}
}
dfs([]);
return res;
};
console.log(allArray([1,2,3]))
// [[ 1, 2, 3 ],[ 1, 3, 2 ],[ 2, 1, 3 ],[ 2, 3, 1 ],[ 3, 1, 2 ],[ 3, 2, 1 ]]
-
子集问题(leetcode78)
和全排列区别:
1》所有可能的子集,每次递归中都将当前组合加入结果中,不用之前的结束条件。
2》[1,3]和[3,1]是相同的,故每次取数时要从其位置取后面的数,加个标志位start。
var sonArray = function(nums) {
var res = [];
var dfs = function(path,start) {
res.push([...path]) //(1)
for(let i=start; i<nums.length; i++) { //(2)
path.push(nums[i]); //(3)
dfs(path,i+1);
path.pop();
}
}
dfs([],0)
return res;
}
console.log(sonArray([1,2,3]))
//[ [], [ 1 ], [ 1, 2 ], [ 1, 2, 3 ], [ 1, 3 ], [ 2 ], [ 2, 3 ], [ 3 ] ]
-
组合求和问题(leetcode39)
和全排列区别:
1》结束条件是满足目标target。
2》组合包括自身和,故每次取数时要从自己位置开始取数。
var targetArray = function(nums, target) {
var res = [];
var dfs = function(path,start) {
if(sum(path) === target) { //(1)
res.push([...path]);
return;
}
if(sum(path) > target) {
return;
}
for(let i=start; i<nums.length; i++) { //(2)
path.push(nums[i]); //(3)
dfs(path.slice(), i);
path.pop();
}
}
dfs([],0)
return res;
}
function sum(arr) {
if(arr.length===0)
return 0;
return arr.reduce((a,b)=>a+b)
}
console.log(targetArray([2,3,5],8))
// [ [ 2, 2, 2, 2 ], [ 2, 3, 3 ], [ 3, 5 ] ]
-
拓展题
输入目标字符串:比如”南京“输出包含目标字符串的节点路径集合。
function func(arr, str) {
var res = [];
var recur = function(arr, path) {
let string = path.join('-');
if(string.indexOf(str) !== -1){
res.push(string);
}
if(arr === undefined || arr.length === 0)
return;
for(let i=0; i<arr.length; i++){
path.push(arr[i].label);
recur(arr[i].children, path);
path.pop();
}
}
recur(arr, []);
return res;
}
console.log(func([{label:'江苏省',children:[{label:'南京市',children:[{label:'xxx区',children:[]}]}]}],'南京'))
//[ '江苏省-南京市', '江苏省-南京市-xxx区' ]