回溯day4

93. 复原 IP 地址

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表判断该层树是否用了相同结点。

参考:programmercarl.com

posted @ 2022-04-18 19:26  一梦两三年13  阅读(21)  评论(0)    收藏  举报