题目链接: 复原 IP 地址
自己的思路:没有想到终止条件,也就是树有三层,拿逗点来当做终止条件!!!
正确思路:这道题和分割回文串很像,因为在进行下一次递归的时候都会判断一段区间是否合法,如果不合法直接下一个分割方案,而且这个题的终止条件为逗点数为3,但是逗点数为3的时候,说明前三个字符已经判断合法了,但是我们在加入之前还是要判断最后一个字符是否合法,如果合法我们才会把它加入到结果集中,难点在于思考什么当做终止条件和在下次递归的时候,startindex要加2,因为递归之前给他加了一个逗点,所以需要加2;回溯三部曲:1、传入参数:字符串、分割起始点以及逗点数;2、终止条件:当逗点数为3且最后一个字符也合法的时候,将s加入结果集中;3、单层逻辑:先判断以当前startindex为起始点分割的一点区间是否合法,如果不合法,考虑下一次分割方案,否则我们给当前节点下一个结点加一个.,然后逗点数+1,然后进入下层递归,再回溯即可!!!
代码:
class Solution {
List<String> res = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if (s.length()>12) return res;
backtracking(s,0,0);
return res;
}
public void backtracking(String s,int startindex,int pointsum){
//如果有三个逗点,则判断最后一个字符是否合法
if (pointsum==3){
if (isValid(s,startindex,s.length()-1))
res.add(new String(s));
return;
}
for (int i = startindex;i<s.length();i++){
//如果不合法,直接下一个循环
if (!isValid(s,startindex,i)) continue;
s = s.substring(0,i+1) + '.' +s.substring(i+1);
pointsum++;
backtracking(s,i+2,pointsum);
pointsum--;
s = s.substring(0,i+1) +s.substring(i+2);
}
}
//检验是否合法
public boolean isValid(String s,int start,int end){
if (end<start) return false;
if (s.charAt(start)=='0'&&start!=end){
return false;
}
int num = 0;
for (int i =start;i<=end;i++){
if (s.charAt(i)<'0'&&s.charAt(i)>'9') return false;
num = num*10+s.charAt(i)-'0';
if (num>255) return false;
}
return true;
}
}
复杂度分析:
时间复杂度: O ( n ) \mathcal{O}(n) O(n)
空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)
题目链接: 子集
自己的思路:题目也不难,但就是想不到!!!!
正确思路:这个题和之前题的区别在于,它每次递归的时候,它都要加入到结果集中,所以它在是终止条件的上方加入path到结果集中;回溯三部曲:1、传入参数:数组和startindex指针;2、终止条件:先加入path到res结果集中,然后再终止,当startindex指向res的后面的时候,终止;3、单层逻辑:加入当前元素到path中,然后递归,然后回溯!!!
代码:
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
backtarcking(nums,0);
return res;
}
public void backtarcking(int[] nums,int startindex){
//一次递归就放一个结果
res.add(new ArrayList(path));
if (startindex==nums.length) return;
for (int i =startindex;i<nums.length;i++){
path.add(nums[i]);
backtarcking(nums,i+1);
path.removeLast();
}
}
}
复杂度分析:
时间复杂度: O ( n ) \mathcal{O}(n) O(n)
空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)
题目链接: 子集 II
自己的思路:这个题自己算是做出来了吧,used数组忘了咋那么写了。
正确思路:和之前存在相同元素的组合问题基本一个思路,只不过这个是收集子集问题;直接回溯三部曲:1、传入参数:数字、startindex指针还有used数组表示元素的使用情况;2、终止条件:先将该子集加入到结果集中,然后判断当前指针是否指向了nums数组的最后,如果是,则返回;3、单层递归:判断相同的两个数,前面的那个数是否已经使用过,如果使用过的话,则直接下一个方案,否则将当前元素加入到path中,然后当前元素的used设置为true,然后递归回溯!!!
代码:
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
boolean[] used;
public List<List<Integer>> subsetsWithDup(int[] nums) {
//必须先排序,不然相同的元素不一定是挨着的
Arrays.sort(nums);
used = new boolean[nums.length];
backtarcking(nums,0,used);
return res;
}
public void backtarcking(int[] nums,int startindex,boolean[] used){
//每个节点都收货子集
res.add(new ArrayList(path));
if (startindex==nums.length) return;
for (int i=startindex;i<nums.length;i++){
//如果存在相同的元素,而且前面的元素没有用过,那么直接跳过
if (i>0&&nums[i]==nums[i-1]&&!used[i-1]) continue;
path.add(nums[i]);
used[i]=true;
backtarcking(nums,i+1,used);
path.removeLast();
used[i]=false;
}
}
}
复杂度分析:
时间复杂度: O ( n ) \mathcal{O}(n) O(n)
空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)