78.子集
初始思路:
回溯三部曲:
1)输入输出:
输入nums数组,因为是在一个集合中处理问题,所以需要startindex。
输出void
2)结束条件:
path中是否有元素均可添加进result数组,但此时不要return,不然后面全部无法执行。
最后startindex>nums.length()再进行return;
3) 单层循环:
同之前一样往其中添加元素即可,变化的只有结束条件。
class Solution {
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
result.add(new ArrayList(path));
backtracking(nums,0);
return result;
}
public void backtracking(int[] nums, int startindex){
if (startindex > nums.length) {
return;
}
for(int i = startindex;i<nums.length;i++){
path.add(nums[i]);
backtracking(nums,i+1);
result.add(new ArrayList(path));
path.remove(path.size()-1);
}
}
}
题解复盘:
class Solution {
List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
public List<List<Integer>> subsets(int[] nums) {
subsetsHelper(nums, 0);
return result;
}
private void subsetsHelper(int[] nums, int startIndex){
result.add(new ArrayList<>(path));//「遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合」。
if (startIndex >= nums.length){ //终止条件可不加
return;
}
for (int i = startIndex; i < nums.length; i++){
path.add(nums[i]);
subsetsHelper(nums, i + 1);
path.removeLast();
}
}
}
注意一些注解的地方,比如:
1)result.add(path)的位置
2)终止条件可不加
其余大体思路基本相同,不做过多解释。
90.子集II
初始思路&&题解复盘:
仅仅是在上题的基础之上增添了used数组,其余不做过多解释。
同时使用used数组之前注意对数组需要进行排序。
93.复原IP地址
初始思路:
首先我的考虑是还按照基础方法去做,将其中间加“.”进行拼接留到后续在进行
1)输入输出:
输入字符串,起始位置(因为是在一个字符串上进行操作)
2)结束条件:
当起始指针指向末尾并且path的size = 4的时候将path装入result,并且进行返回
3)单层循环:
单层上:
1)注意for循环,每次指切割1~3个字符
2)注意舍去的条件:以0开头的只能切0一个字符;
三个字符最大不能超过255.
其余就是正常情况,最后再借助StringJoiner进行拼接
import java.util.StringJoiner;
class Solution {
List<String> path = new ArrayList<>();
List<List<String>> result = new ArrayList<>();
List<String> result1 = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
backtracking(s,0);
pinjie(result);
return result1;
}
public void backtracking(String s,int startindex){
if(startindex==s.length()&&path.size()==4){
result.add(new ArrayList<>(path));
return;
}
int end = startindex+4<s.length()?startindex+4:s.length();
for(int i = startindex+1;i<=end;i++){
String str = s.substring(startindex, i);
int length = s.substring(startindex, i).length();
if(str.startsWith("0")&& length !=1){continue;}
else if(Integer.parseInt(str)>255){continue;}
path.add(s.substring(startindex,i));
backtracking(s,i);
path.remove(path.size()-1);
}
}
public void pinjie(List<List<String>> res){
for (List<String> re : res) {
StringJoiner sj = new StringJoiner(".");
for (String s : re) {
sj.add(s);
}
result1.add(sj.toString());
}
}
}
题解复盘:
1)递归参数
startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。
本题我们还需要一个变量pointNum,记录添加逗点的数量。
所以代码如下:
vector<string> result;// 记录结果
// startIndex: 搜索的起始位置,pointNum:添加逗点的数量
void backtracking(string& s, int startIndex, int pointNum) {
2)终止条件
pointNum表示逗点数量,pointNum为3说明字符串分成了4段了。
然后验证一下第四段是否合法,如果合法就加入到结果集里
代码如下:
if (pointNum == 3) { // 逗点数量为3时,分隔结束
// 判断第四段子字符串是否合法,如果合法就放进result中
if (isValid(s, startIndex, s.size() - 1)) {
result.push_back(s);
}
return;
}
3)单层搜索的逻辑
for (int i = startIndex; i < s.size(); i++) {
if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法
s.insert(s.begin() + i + 1 , '.'); // 在i的后面插入一个逗点
pointNum++;
backtracking(s, i + 2, pointNum); // 插入逗点之后下一个子串的起始位置为i+2
pointNum--; // 回溯
s.erase(s.begin() + i + 1); // 回溯删掉逗点
} else break; // 不合法,直接结束本层循环
}
class Solution {
List<String> result = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if (s.length() > 12) return result; // 算是剪枝了
backTrack(s, 0, 0);
return result;
}
// startIndex: 搜索的起始位置, pointNum:添加逗点的数量
private void backTrack(String s, int startIndex, int pointNum) {
if (pointNum == 3) {// 逗点数量为3时,分隔结束
// 判断第四段⼦字符串是否合法,如果合法就放进result中
if (isValid(s,startIndex,s.length()-1)) {
result.add(s);
}
return;
}
for (int i = startIndex; i < s.length(); i++) {
if (isValid(s, startIndex, i)) {
s = s.substring(0, i + 1) + "." + s.substring(i + 1); //在str的后⾯插⼊⼀个逗点
pointNum++;
backTrack(s, i + 2, pointNum);// 插⼊逗点之后下⼀个⼦串的起始位置为i+2
pointNum--;// 回溯
s = s.substring(0, i + 1) + s.substring(i + 2);// 回溯删掉逗点
} else {
break;
}
}
}
// 判断字符串s在左闭⼜闭区间[start, end]所组成的数字是否合法
private Boolean isValid(String s, int start, int end) {
if (start > end) {
return false;
}
if (s.charAt(start) == '0' && start != end) { // 0开头的数字不合法
return false;
}
int num = 0;
for (int i = start; i <= end; i++) {
if (s.charAt(i) > '9' || s.charAt(i) < '0') { // 遇到⾮数字字符不合法
return false;
}
num = num * 10 + (s.charAt(i) - '0');
if (num > 255) { // 如果⼤于255了不合法
return false;
}
}
return true;
}
}
在判断非法的时候比我多判断了字符的情况,但是相较于后拼接的情况,考虑了更多起始和结尾的情况。