回溯算法
93.复原IP地址
分割法:
- 注意, 需要用变量point记录分割的段数, 不然分割的段数超过了4段, 必然是不合法的
- 在分割了四段的时候, 判断最后一段的末尾是不是整个字符串的末尾(判断是否四段时候刚好用掉了全部的字符)
- 需要对切割的字符串进行合法性判断(去掉前导0和小于255)
代码:
class Solution {
List<String> ans = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if(s.length() > 12) return ans;
backtracking(s, "", 0, 0);
return ans;
}
// 用point记录分割的段数, 若已经分割了四段, 则应该进行判断start是否到字符串末尾
// 若到了末尾, 则代表分割合理, 应该收集结果, 否则不合理
public void backtracking(String s, String path, int start, int point) {
// 递归出口: 分割四段
if(point == 4) {
if(start == s.length()) {
ans.add(path.substring(0, path.length()-1));
}
return;
}
for(int i=start; i<s.length(); i++){
String str = s.substring(start, i+1);
// 去掉前导0
if((str.length() > 1 && str.charAt(0)-'0' == 0) || Integer.parseInt(str) > 255){
return;
}
backtracking(s, path+str+".", i + 1, point + 1);
}
}
}
78.子集
注意:
- 组合和回溯问题一般都收集叶子节点的值
- 子集问题一般对树上面的每个节点的值都需要进行收集
代码:
class Solution {
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> list = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
ans.add(new ArrayList<>(list));
backtracking(nums, 0);
return ans;
}
public void backtracking(int[] nums, int start) {
// start代表本层的起点, i代表本层目前走到的点
for(int i = start; i < nums.length; i++){
list.add(nums[i]);
// 把路径上的所有节点值都收集到
ans.add(new ArrayList<>(list));
backtracking(nums, i + 1);
list.removeLast();
}
}
}
90.子集II
- 解法同上
- 数组里面有重复元素,结果要求无重复---排序+横向去重
代码:
class Solution {
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> list = new LinkedList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
ans.add(new ArrayList<>(list));
backtracking(nums, 0);
return ans;
}
public void backtracking(int[] nums, int start){
for(int i=start; i<nums.length; i++){
// 一般数组里面有重复元素, 但是结果里面不允许重复组合,需要横向去重
if(i > start && nums[i]==nums[i-1]) continue;
list.add(nums[i]);
ans.add(new ArrayList<>(list));
// 一般都需要纵向去重, 除非是排列, 前后顺序不同也算一种答案
backtracking(nums, i+1);
list.removeLast();
}
}
}