回溯算法,在leetcode中的算法题有很多种,结合几道leecode的题,加深一下对此理解,方便以后愉快的刷题。
回溯算法
概念
回溯算法,即DFS(深度优先算法),看到深度优先,想到了啥?是不是树的遍历,一条道走到黑,然后又回退到上一步,是一种试探性的算法。解题的套路,我就直说吧,就是用递归。
例题
以一道题来说明:
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式,IP地址有以下几点特点:
- 4个整数(每个整数位于 0 到 255 之间)
- 不能以0开始,但是可以只有一个0
- 整数之间用 '.' 分隔
简单画下它的树形图,一般这种用回溯算法的题目,我都是直接画个图先。
可以看到:回溯算法实际上一个类似枚举的深度优先搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回(也就是递归返回),尝试别的路径。
解题
前面提到,既然回溯算法要用到递归,我用递归解下这道题:
var restoreIpAddresses = function(s) {
let res = [];
dfs(s, 1, -1, []);
/**
* 回溯算法
* @param {*} str 传入的字符串
* @param {*} level ip的第几个整数,从0开始
* @param {*} index 要选取的字符串的位置
* @param {*} ipAddress ip地址,以数组形式传入,,满足条件后以.连接起来。比如[192, 168, 80, 1] 可连接为192.168.80.1
*/
function dfs(str, level, index, ipAddress) {
//1.截止条件
if(level === 5 || index === str.length -1 ){
if(level === 5 && index === str.length -1){
res.push(ipAddress.join('.'));
}
return;
}
// 2.候选节点
//每个整数位于 0 到 255 之间组成,最多截取前三位
for(let i =1; i <4; i++){
let ip = str.substr(index+1, i);
//判断是否满足条件 0~255之间,不能以0开始
if(parseInt(ip)<256 && ( ip === '0' || !ip.startsWith('0'))){
ipAddress.push(ip);
dfs(str,level+1,index+i,ipAddress);
ipAddress.pop();
}
}
}
return res;
}
解题模板
- 判断递归终止条件
- 找到候选节点push进栈
- 完成递归后pop出数据,即回溯
这个只是我自己认为的解决这类问题的模板。
回溯题目解答
主要刷几道leecode题加深自己理解,都是套上面的模板
电话号码的字母组合
/**
* @param {string} digits
* @return {string[]}
*/
var letterCombinations = function(digits) {
// 处理边界条件
if(digits.length<1){
return []
}
let res=[];
//创建一个map数据结构保存数字与字母对应关系
let map = new Map()
.set('2', 'abc')
.set('3', 'def')
.set('4', 'ghi')
.set('5', 'jkl')
.set('6', 'mno')
.set('7', 'pqrs')
.set('8', 'tuv')
.set('9', 'wxyz');
dfs(digits, 0 , '');
/**
* digits: 传入的数字字符串
* index: 遍历到第几个数字了
* str: 字母组合
*/
function dfs(digits, index, str){
//1. 截止条件
if(index === digits.length){
res.push(str);
return;
}
// 2. 候选节点
for(let s of map.get(digits[index])){
str+= s;
dfs(digits, index+1,str);
// 3. 回溯,从栈中弹出
str= str.slice(0, str.length-1);
}
}
return res;
};
组合总数
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum = function(candidates, target) {
let res = [];
let strArr = [];
dfs(candidates, target, []);
function dfs(candidates, remain, arr){
if(remain <= 0){
if(remain === 0 ){
let str = [...arr].sort(function(a,b) {
return a-b;
});
// 这里主要是去重
if(strArr.indexOf([...str].toString()) === -1){
strArr.push([...str].toString());
res.push([...str]);
}
}
return;
}
for(let can of candidates){
remain -= can;
arr.push(can);
dfs(candidates, remain, arr);
remain+=can;
arr.pop();
}
}
return res;
};