回溯伪代码:
void backtracking(参数){
if(终止条件){
存放结果;
return;
}
for(选择本层集合中元素(画成树,就是树子节点大小){
处理节点;
backtracking();
回溯,撤销处理结果;
}
}
主要解决:排列(全排列),集合,组合问题
例如:全排列
输入:nums = [1,2,3]
输出:[1,2,3].[1,3,2].[2,1,3],[2,3,1].[3,1,2],[3,2,1]
输出二叉树的所有路经
可以注意到有几个叶子节点,就有几条路径。从回溯的角度分析,得到路径ABD后怎么找第二条路径ABE,这里就要将D撤销(回),然后再递归(溯)
输入:root=[1,2,3,null,5]
输出:["1->2->5","1->3"]
回溯部分:
private void dfs(TreeNode root, List<Integer> temp){
if(root == null){
return; //终止条件
}
temp.add(root.val){
//如果是叶子节点记录
if(root.left == null && root.right == null){
ans.add(getPashString(temp));
}
dfs(root.left,temp);
def(root.right, temp);
temp.remove(temp.size()-1);//撤销
}
动态规划
最简单的例子:找出最长的递增的子序列长度(3)
输入:nums = [1,5,2,4,3]
输出:[1,2,4],[1,2,3]
观察遍历树可以发现有许多重复计算,如在遍历1,2,4的时候就已经计算过“从4开始的最大子序列的长度”。后面遍历去1,4时已经知道不是最长序列了(要经过2)有重复的计算——所以可以在第一次计算的时候将结果保存,之后遍历相同的节点就不需要重复计算(核心:递归+剪枝)
例题:统计路经之和
//只使用递归实现
public class uniquePaths{
public void uniquePath(int m, int n){
return search(m , n);
}
public int search(int m, int n){
if(m == 1|| n == 1){
return 1;
}
return search(m-1, n) + search(m, n+1);
}
}
使用二维数组优化递归
实现
public int uniquePaths(int m, int n){
int[][] f = new int[m][n];
f[0][0] = 1;
for (int i=0; i<m; i++){
for(int j = 0; j<n; j++){
if(i>0 && j>0){
f[i][j] = f[i-1][j] + f[i][j-1];
}else if(i > 0){
f[i][j] = f[i-1][j];
}else if(j> 0){
f[i][j] = f[i][j - 1];
}
}
}
return f[m-1][n-1];
}
滚动数组:用一维代替二维数组
缓存空间用二维数组比较占空间
得到:
public int uniquePaths(int m, int n) {
int[] dp = new int[n];
Arrays.fill(dp, 1);
for(int i = 1; i < m; ++i) {
for(int j = 1; j < n; ++j) {
//等式右边的 dp[j]是上一次计算后的,加上左边的dp[j-1]即为当前结果
dp[j] = dp[j] + dp[j - 1];
}
}
return dp[n - 1];
}