参考文献链接:代码随想录
本人代码是Java版本的,如有别的版本需要请上代码随想录网站查看。
93.复原IP地址
解题思路
这道题目感觉跟昨天的分割回文串很像。
首先明确思路,先是一个一个字符去添加到StringBuilder,当该StringBuilder满足0到255要求时开启回溯去寻找第二个满足要求的。当题目给定字符串刚好被遍历完并且添加"."的次数是4次,那就说明我们刚好拼好了满足题意的ip地址。
我的代码时间复杂度和空间复杂度没有代码随想录的低,不过还可以,可以先通过我的思路理解这道题目再去代码随想录去优化代码。而且我的代码递归的参数也确实比较多。
代码示例
在代码注释中详细解释了递归的参数都是什么含义,并且解释了回溯隐藏在哪里
class Solution {
List<String> result = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if(s.length() > 12 || s.length() < 4){
return result;
}
backtracking(s,0,new StringBuilder(),"",0);
return result;
}
// s是题目给出的字符串,start是每次字符串遍历位置,stringBuilder用来拼接满足0-255的字符串
//string用来把ip拼接起来,count用来计数拼接了几次了,超次数就return,剪枝操作
public void backtracking(String s,int start,StringBuilder stringBuilder,String string,int count){
if(count > 4){
return;
}
if(start == s.length()){
if(count == 4){
result.add(string.substring(0,string.length() - 1));
}else{
return;
}
}
for(int i = start;i < s.length();i++){
stringBuilder.append(s.charAt(i));
if(isVaild(stringBuilder.toString())){
//回溯隐藏在这里,两个回溯,一个是string字符串的,一个是count的回溯
backtracking(s,i + 1,new StringBuilder(),string + stringBuilder.toString() + ".",count + 1);
}
}
}
public boolean isVaild(String s){
if (s.charAt(0) == '0' && s.length() != 1) { // 0开头的数字不合法
return false;
}
Long temp = Long.parseLong(s);
if(temp < 0 || temp > 255){
return false;
}
return true;
}
}
78.子集
解题思路
这道题目是找子集的,但模板跟前面的组合大差不差,我们直接上代码解释。
代码示例
为什么这次不用判断条件就可以往result添加了呢,因为这次是子集,任意一种情况都是可以添加的。
然后我们再通过start确保每种情况不会重复,比如1,2这种情况,那2开始后只有2,3.没有2,1.
class Solution {
List<Integer> paths = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums,0);
return result;
}
public void backtracking(int[] nums,int start){
result.add(new ArrayList<>(paths));
if(start == nums.length){
return;
}
for(int i = start;i < nums.length;i++){
paths.add(nums[i]);
backtracking(nums,i+1);
paths.remove(paths.size() - 1);
}
}
}
90.子集II
解题思路
这道题目跟上一题唯一区别就是数组中有重复的元素,那我们其实之前几天作过这样的题目,只要用一个boolean数组去记录使用过哪个元素就好。
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
continue;
}
当我们发现当前元素和前一个元素相同,那我们就判断前一个元素是否被使用了,如果被使用说明是树根之间的,可以使用。
如果没使用说明他俩是树层之间的需要去重。
代码示例
class Solution {
List<Integer> paths = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
boolean[] used;
public List<List<Integer>> subsetsWithDup(int[] nums) {
used = new boolean[nums.length];
Arrays.sort(nums);
Arrays.fill(used,false);
backtracking(nums,0);
return result;
}
public void backtracking(int[] nums,int start){
result.add(new ArrayList<>(paths));
if(start == nums.length){
return;
}
for(int i = start;i < nums.length;i++){
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
continue;
}
paths.add(nums[i]);
used[i] = true;
backtracking(nums,i+1);
paths.remove(paths.size() - 1);
used[i] = false;
}
}
}