回溯day4
class Solution {
private List<String> res;
private Deque<StringBuilder> deque;
public List<String> restoreIpAddresses(String s) {
res = new ArrayList<>();
deque = new LinkedList<>();
char[] a = s.toCharArray();
//当前填充开始字符串的位置和剩余需要填充ip段
backtracking(a, 0, 4);
return res;
}
private void backtracking(char[] a, int s, int n) {
int len = a.length;
//填充完毕 递归调用结束语句 优化1(24ms->2ms):s==len就是填入所有字符串了
if (s == len && n == 0) {
StringBuilder sbRes = new StringBuilder();
for (StringBuilder sb : deque) {
sbRes.append(sb).append('.');
}
//移除最后一个'.'
sbRes.deleteCharAt(sbRes.length() - 1);
res.add(sbRes.toString());
return;
}
if (s == len || n == 0) return;
/* max是最大可填入字符串 min是最小填入字符串 len - s是剩余未填充字符串
* min通过假设后面剩余坑全部填最大长度3求得 最小长度不能小于1
* max通过假设后面剩余坑全部填最小长度1求得 最大长度不能大于3
*/
int min = (len - s) - 3 * n > 1 ? (len - s) - 3 * n : 1
, max = len - s - (n - 1) < 3 ? len - s - (n - 1) : 3;
//从当前位置开始最多填充三个数字 满足每个整数位于 0 到 255 之间组成,且不能含有前导 0
//如果min > 3 表示当前需填入3位以上后续才能满足条件 显然不行,剪枝
if (min > 3) return;
StringBuilder sb = new StringBuilder();
for (int i = s; i < s + max; i++) {
//先填入最小位数
while (i < s + min -1) {
sb.append(a[i]);
i++;
}
sb.append(a[i]);
//优化2:剪枝顺序调整
//1.位数大于1 前导为0 2.不于 0 到 255 之间 3.位数>3 三种不符合题设条件的情况剪枝
if (sb.length() > 3) return;
if (sb.length() == 3 && Integer.parseInt(sb.toString()) > 255) return;
if (sb.length() > 1 && sb.charAt(0) == '0') return;
deque.addLast(sb);
backtracking(a, i + 1, n - 1);
//回溯
deque.removeLast();
}
}
}
78. 子集
class Solution {
private List<List<Integer>> res;
private LinkedList<Integer> path;
public List<List<Integer>> subsets(int[] nums) {
res = new ArrayList<>();
//空集是任何集合子集
res.add(new ArrayList<Integer>());
if (nums.length == 0 || nums == null) return res;
path = new LinkedList<>();
backtracking(nums, 0);
return res;
}
private void backtracking(int[] nums, int s) {
int len = nums.length;
if (s == len) {
//到末尾无需继续
return;
}
for (; s < len; s++) {
path.add(nums[s]);
res.add(new ArrayList<>(path));
backtracking(nums, s + 1);
path.removeLast();
}
}
}
90. 子集 II
class Solution {
private List<List<Integer>> res;
private LinkedList<Integer> path;
public List<List<Integer>> subsetsWithDup(int[] nums) {
res = new ArrayList<>();
//加入空集
res.add(new ArrayList<>());
if (nums.length == 0 || nums == null) return res;
path = new LinkedList<>();
//排序去重
Arrays.sort(nums);
backtracking(nums, 0);
return res;
}
private void backtracking(int[] nums, int s) {
int len = nums.length;
if (len == s) return;
for (int i = s; i < len; i++) {
//去重
if (i > s && nums[i] == nums[i - 1]) continue;
path.add(nums[i]);
// System.out.println("s:"+s+" i:"+i);
// for (int j : path) {
// System.out.print(j+" ");
// }
// System.out.println();
res.add(new ArrayList<>(path));
backtracking(nums, i + 1);
path.removeLast();
}
}
}
491. 递增子序列
class Solution {
private List<List<Integer>> res;
private LinkedList<Integer> path;
private int len;
public List<List<Integer>> findSubsequences(int[] nums) {
res = new ArrayList<>();
len = nums.length;
if (len <= 1 || nums == null) return res;
path = new LinkedList<>();
backtracking(nums, 0, nums[0]);
return res;
}
private void backtracking(int[] nums, int s, int pre) {
if (s == len) return;
Set<Integer> set = new HashSet<>();
//题设给出数值范围[-100, 100] 可以用数组做哈希
//int[] hash = new int[201];
for (int i = s; i < len; i++) {
//排除不符合条件的情况并去重
if (set.contains(nums[i])) continue;
//if (hash[nums[i] + 100] == 1) continue;
if (s > 0 && nums[i] < pre) continue;
path.add(nums[i]);
//表示本层已经使用过nums[i]
set.add(nums[i]);
//hash[nums[i] + 100] = 1;
if (path.size() > 1)
res.add(new ArrayList<>(path));
backtracking(nums, i + 1, nums[i]);
path.removeLast();
}
}
}
此题需要注意去重的问题,由于题设所求不允许我们排序,需要用到hash表判断该层树是否用了相同结点。