1. 关于回溯
1.1 总体思路
- 为什么使用回溯?
- 组合问题:提取哪些节点组成的path?
- 分割问题:在字符串的哪个位置下刀?
- 子集问题:
- 需要递归几层?
- 返回条件是什么
- 每层递归要尝试哪些节点?
- 注意不要漏掉回退
public void backtrack(//参数){
//终止条件,确定需要递归几层,即树的深度
if(condition){
result.add(...);
return;
}
//单层递归逻辑,记录本层集合的元素,即存储本层树中所有的节点,即树的宽度
for(//从哪里开始遍历?从0遍历到尾,还是从上次历史记录<startIndex>开始遍历剩余的数组?){
处理节点,如path.add();
调用递归,进入下一层;
回退,撤销处理结果;
}
1.2 分治解法
问题
- 给定的数组是单一集合/多个集合? (如果是单一集合,则每层递归都要考虑递归的起始位置问题!)
- 在元素不重复/重复的情况下?
- 满足的条件是?(如组合总和=?/组合中包含n个数字…)
- 并返回所有的集合可能性,并确保集合不重复出现
解答
-
初始条件:给出一个数组nums
-
返回要求:
每层遍历返回一条path,经过n层遍历后,return path的集合result
2.1 情况1:以每个节点串联出一条path,如组合、分割类问题
例题:很多
2.2 情况2:每个节点内的组合就是一条path,如子集问题
例题:子集78,90
2.3 情况3: 返回的不是套娃数组,而是二维数组(或转换后的字符串),如棋盘问题。需要引入board[row][col]二维数组,在此基础上思考
例题:51,37 -
终止条件:
3.1 情况1:以满足特定(condition)作为终止,如求和if(sum==target) return;
3.2 情况2:以遍历到最后一行/遍历满k个元素作为终止,如返回所有k个数的组合if(path.size() == k) return;,或遍历到最后一行if(row == n) /if(start > nums.length-1) -
单层递归:
4.1. 问:什么时候允许进入下一层遍历?- 4.1.1 无脑进入
- 4.1.2 情况比较复杂,需要单独写一个判定方法,如排列结果是否符合ip地址规则、是否是回文字符串、是否符合数独/N皇后排列条件等 (如51,37,93,131)
4.2 问:每层遍历中,需要递归的是除了nums[i]的部分,还是nums[i]之后的剩余部分?还是从0递归到尾?
- 4.2.1 情况1:nums[i]之后的剩余部分,i= startIndex; i<nums.length
- 4.2.2 情况2:除了nums[i]的部分,需要借助boolean[] used这个数组作为开关,标记nums中不同的index(及其对应的元素)是否用过,用过则打开开关:true
- 4.2.3 情况3:从0到尾。如数据来自多个集合如电话号码数字对应字母 int i=0; i<str.length() (如lc17)
-
调用执行单层递归
4.1 情况1:无重复元素。正常操作即可,处理元素-调用递归-回退(*注意:如果有sum等计算,回退时不要忘记)
4.2 情况2:需要去重。将数组有序化之后 Arrays.sort(nums),在每层从左到右遍历时比较前后元素是否相重 if(i>1 && arr[i]==arr[i-1]) contiunue; else…
例题:组合(40) -
关于回退
注意:如果需要在递归中执行操作,如sum、增加字符、改变used[i]=true等,记得回退时一并删除
2. 关于字符串
字符串string
- 字符串长度:str.length();
- 提取字符串某索引位的值:str.charAt(i);
- 提取字符串切片: str.substring(left,right) --注意:切片范围左闭右开,不包括right
- 将字符转为数字:‘integer’ - ‘0’; 或 Integer.parseInt(str);
- 判断字符串引用是否相等: strA.equals(strB)
- 字符串排序:String.sort(str);
- 用stringbuilder拼接字符串:
- 新增:sb.append()
- 删除:sb.deleteCharAt(index)
- 获取长度:sb.length()
- 翻转字符串:string reverseStr = new StringBuilder(str).reverse().toString
记录path:ArrayList vs LinkedList
- 回退
- arraylist删除最后一位:path.remove(path.size()-1);
- 链表删除最后一位:path.removeLast();
- 将链表转为List< Integer >:result.add(new ArrayList<>(path));
将字符数组char[ ][ ]转为字符串string
遍历char[],String.copyValueOf(每一行字符数组)
//两个目的:1.把第n行二维数组合并成一个字符串 2.把n行字符串录入一个list
public List<String> boardToPath(char[][] board){
List<String> path = new ArrayList<>();
for(char[] c : board){
path.add(String.copyValueOf(c)); //String.copyValueOf: 把字符串数组转为字符串
}
return path;
}