93.复原IP地址
题目链接:力扣
这道题也是对字符串进行切割,和131题非常相似,但是需要判断是否符合要求(在0-255之间,且不能含有前导0)。
树形结构:
回溯三部曲:
1.输出以及输入的参数:输出-void;输入-【两个全局变量:List<String> result,String ns - 储存目前分割的字符串】;backtracking(String s,int index, int pointSum - 当前字符串被分割的段);
2.终止条件:if(index == s.length && pointSum == 4) 将当前字符串储存,返回;if(index == s.length || pointSum == 4 )返回;
3. 单层循环逻辑:for(int i= index; i<s.length() & i<index+3; i++) 截取[index, i]这段数字,判断是否符合,如果不符合则break,结束循环。若是合法,则将这段数字加入ns中,pointSum++, 递归 - backtracking(s, i+1);回溯 - pointSum--, 删除ns中的数字。
具体代码实现:
class Solution {
public List<String> restoreIpAddresses(String s) {
result = new ArrayList<>();
ns = new StringBuffer();
if(s.length()<4 || s.length()>12) return result;
backtraking(s, 0, 0);
return result;
}
List<String> result;
StringBuffer ns;
public void backtraking(String s, int index, int points){
//终止条件 - 这里的points指的是被分割的段数
if(points == 4 && index == s.length()){
result.add(ns.toString());
return;
}
if(index == s.length() || points == 4){
return;
}
//剪枝
for(int i=index; i<s.length()&& i<index+3; i++){
String m = s.substring(index, i+1);
if(isValid(m)){
ns.append(s.substring(index, i+1));
//如果超过3个则不需要增加点
if(points <3){
ns.append(".");
}
points++;
backtraking(s, i+1, points);
//回溯
points--;
//删除的时候要把增加的点都算上去
ns.delete(index+points, i+points+2);
}
}
}
public boolean isValid(String m){
if(m.length() == 0){
return false;
}
//判断是否存在先导零
if(m.charAt(0) == '0' && m.length() != 1){
return false;
}
//判断是否都是数字
for(int i=0; i<m.length(); i++){
if(!Character.isDigit(m.charAt(i))){
return false;
}
}
判断数字的大小是否符合
int n = Integer.parseInt(m);
if(n>255 || n<0){
return false;
}
return true;
}
}
参考资料:代码随想录
78.子集
题目链接:力扣
这道题和之前的组合、分割问题的区别是:
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
回溯三部曲:
1.输出和输入参数:输出 - void;输入- 【两个全局变量 - List<List<Integer>> result; List<Integer> list】, backtraking(int[] nums, int startIndex);
2.终止条件:if(startIndex == nums.length()) 返回;
3.单层循环逻辑:for(int i = startIndex; i<nums.length; i++) 将nums[i]放入list;将list放入result;backtracking(nums, i+1);回溯 - 将nums[i]从list删除;
具体代码实现:
class Solution {
public List<List<Integer>> subsets(int[] nums) {
result = new ArrayList<>();
list = new ArrayList<>();
//放入空子集
result.add(new ArrayList<>(list));
backtraking(nums, 0);
return result;
}
List<List<Integer>> result;
List<Integer> list;
public void backtraking(int[] nums, int startIndex){
if(startIndex == nums.length){
return;
}
for(int i=startIndex; i<nums.length; i++){
list.add(nums[i]);
//将每一个叶子结点放入结果集
result.add(new ArrayList<>(list));
backtraking(nums, i+1);
//回溯
list.remove(list.size()-1);
}
}
}
参考资料:代码随想录
90.子集II
这道题和78.子集很像,但是数组中含有重复元素,因此需要进行去重 - 注意:这里去重不能只是在循环的时候判断nums内部的值,还需要使用一个used数组进行标记。
同样是需要储存所有叶子结点的结果集。
回溯三部曲:
1.输出和输入参数:输出 - void;输入- 【两个全局变量 - List<List<Integer>> result; List<Integer> list】, backtraking(int[] nums, int startIndex, int[] used);
2.终止条件:if(startIndex == nums.length()) 返回;
3.单层循环逻辑:for(int i = startIndex; i<nums.length; i++) 【去重 - 如果 i>0 && nums[i] == nums[i-1] && used[i-1] == false, continue】; 将nums[i]放入list;将list放入result;将used[i] 标记为 ture;backtracking(nums, i+1);回溯 - 将nums[i]从list删除;将used[i] 标记为false;
具体代码实现:
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
result = new ArrayList<>();
list = new ArrayList<>();
result.add(new ArrayList<>(list));
//排序
Arrays.sort(nums);
used = new boolean[nums.length];
backtraking(nums, 0, used);
return result;
}
List<List<Integer>> result;
List<Integer> list;
boolean[] used;
public void backtraking(int[] nums, int startIndex, boolean[] used){
if(startIndex == nums.length){
return;
}
for(int i=startIndex; i<nums.length; i++){
//去重
if(i>0 && nums[i] == nums[i-1] && used[i-1] == false){
continue;
}
used[i] = true;
list.add(nums[i]);
//将叶子结点放入结果集
result.add(new ArrayList<>(list));
backtraking(nums, i+1, used);
//回溯
used[i] = false;
list.remove(list.size()-1);
}
}
}
参考资料:代码随想录