93. 复原 IP 地址
同样是抽象成树形问题然后做切割处理,这样的话就可以联想到这题类似131可以用回溯来解决。
- 确定递归出入参:因为切割过的字符不能反复切割,所以需要startIndex确定当前切割线走到的位置,同时需要一个pointNum来记录添加逗点的数量。
- 递归终止条件:这里不能用切割线走到字符串终点来当作终止条件,因为前提已经说了IP地址是有四段的,所以当pointNum这个逗点有三个(说明这时字符串已经被切成四段了),并且切开的段都符合IP地址的要求时(0<x<255)这个结果是符合IP地址的,可以添加到结果集里面。
- 单层搜索逻辑:在
for (int i = startIndex; i < s.size(); i++)循环中 [startIndex, i] 这个区间就是截取的子串,需要判断这个子串是否合法。如果合法就在字符串后面加上符号.表示已经分割。如果不合法就结束本层循环。以及需要注意递归和回溯的过程,因为需要在字符串中加入了分隔符.,回溯的时候需要把这个逗点删掉,同时pointNum-1;此外回溯期间因为添加了逗点的原因,遍历到下一个字符的位置应该就是i+2了。
class Solution {
//存放最后的结果集
List<String> res = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
//存放每次划分出来的str
LinkedList<String> path = new LinkedList<>();
backTrack(s, path, 0);
return res;
}
//回溯算法
private void backTrack(String s, LinkedList<String> path, int startIndex){
//划分出来的str子串个数如果>4,说明不是IP地址,剪枝
//比如s = "25525511135",假设最后path中存的是25.52.55.111.35,那么此时这个path的大小已经>4了,不符合IP地址的格式
if(path.size() > 4) return;
//startIndex是划分字串s的分割线,如果把划分的字串写成树状图,这个startIndex也就是当前遍历到这个树的层数;因此,当startIndex把整一个字串分割完了(这时startIndex也就走到了字串的末尾),并且path的长度大小刚好符合一个IP地址的规格时,我们把这一个path作为一个结果集存放到res中
if(startIndex == s.length() && path.size() == 4){
res.add(toRes(path));
return;
}
//开始遍历(主要回溯过程)
for(int i = startIndex; i < s.length(); i ++){
//这一步是把当前遍历到的所在的子串从父字串s中裁剪下来
String str = s.substring(startIndex, i + 1);
//对这一个子串做判断(是否符合IP地址中digit的要求);如果符合,那么condition是false,这个continue就不执行,接着从30行往后开始走;如果不符合,condition为true,执行continue意味着30-32行都不执行,直接执行下一个for循环(也就是回到27行,继续裁出来一个新的子串来判断是否合规)
if(!isVaild(str)) continue;
//把合规的子串添加进path中,集满4个并且startIndex恰好走到结尾的话,就是19-22行代码了!(也就是我们想要的正确地址)
path.add(str);
//回溯
backTrack(s, path, i + 1);
path.removeLast();
}
}
//把存在path中的合法IP地址子串遍历,在后面加.
private String toRes(LinkedList<String> path){
//声明一个StringBuilder用他来存储
StringBuilder sb = new StringBuilder();
for(int i = 0; i < path.size(); i ++){
sb.append(path.get(i));
if(i != path.size() - 1){
sb.append(".");
}
}
return sb.toString();
}
//判断当前截取的字符子串是否合规
public boolean isVaild(String s){
//如果截取的子串长度是1,合规(IP地址中的数字可以是一位)
if(s.length() == 1) return true;
//如果截取的子串长度大于3,不合规,因为有效的IP最大也就到255
if(s.length() > 3) return false;
//如果截取的子串首字符是0,不合规
if(s.charAt(0) == '0') return false;
//如果截取的子串数值>255也是不合规
if(Integer.valueOf(s) > 255) return false;
return true;
}
}
78. 子集
对比起上一题这题仿佛一个简单题。
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
backtrace(nums, 0);
return res;
}
public void backtrace(int[] nums, int idx){
res.add(new ArrayList(path));
if(idx >= nums.length) return;
for(int i = idx; i < nums.length; i ++){
path.add(nums[i]);
backtrace(nums, i + 1);
path.removeLast();
}
}
}
90. 子集 II
第一次提交因为判断条件没写清楚,修改一下直接过了
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
boolean[] isused;
public List<List<Integer>> subsetsWithDup(int[] nums) {
isused = new boolean[nums.length];
Arrays.sort(nums);
Arrays.fill(isused, false);
backtrace(nums, 0);
return res;
}
public void backtrace(int[] nums, int startIndex){
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] && !isused[i - 1]){
continue;
}
path.add(nums[i]);
isused[i] = true;
backtrace(nums, i + 1);
isused[i] = false;
path.removeLast();
}
}
}