leetcode 刷题_day30_回溯04_分割额外判断和分层控制&子集

93.复原IP地址

思路

使用回溯进行字符串切割,通过 startIndex控制切割的位置
递归深度决定切割次数,但只能递归两层,第三层需要取末尾,在终止条件中判断
每个切割的字符串需要进行合法性判断

剪枝优化

  1. 判断切割的字符串是否有效,如果无效,直接将当前节点对应的整棵树的右下角全部剪掉
    image.png

解题方法

描述你的解题方法

复杂度

  • 时间复杂度:

添加时间复杂度, 示例: O ( n ) O(n) O(n)

  • 空间复杂度:

添加空间复杂度, 示例: O ( n ) O(n) O(n)

Code


  class Solution {
    private List<String> result = new ArrayList<>();
    private LinkedList<String> path = new LinkedList<>();
    int level = 0;

    // 使用回溯进行字符串切割,通过 startIndex 控制切割的位置
    // 递归深度决定切割次数,因此只能递归两层,第三层直接取末尾,在终止条件中判断
    // 每个切割的字符串需要进行合法性判断
    public List<String> restoreIpAddresses(String s) {
        if (s.length() > 0) {
            backTracking(s, 0);
        }
        return result;
    }

    private void backTracking(String s, int startIndex) {
        // 终止条件
        if (level == 3) {
            String temp = s.substring(startIndex, s.length());
            if (isValid(temp)) {
                path.add(temp);
                result.add(path.stream().collect(Collectors.joining(".")));
                path.removeLast();
            }
            

            return;
        }

        // 横向遍历
        for (int i = startIndex; i < s.length(); i++) {
            // 当前节点处理
            // 字符串范围 [startIndex, i]
            // 判断切割的字符串是否有效,如果无效,直接将当前节点对应的整棵树的右下角全部剪掉
            String temp = s.substring(startIndex, i + 1);
            if (!isValid(temp)) {
                return;
            }
            path.add(temp);
            level += 1;

            // 纵向遍历
            backTracking(s, i + 1);

            path.removeLast();
            level -= 1;
        }

        return;
    }

    private boolean isValid(String temp) {
        if (temp.length() == 0) {
            return false;
        }
        if ("0".equals(temp)) {
            return true;
        }
        if (temp.charAt(0) - '0' == 0) {
            return false;
        }
        long num = Long.valueOf(temp);
        if (num < 0l || num > 255l) {
            return false;
        }
        return true;
    }
}

78.子集

思路

通过回溯,构建逻辑树

  1. 横向遍历中,通过 startIndex 控制获取的数组元素位置
  2. 纵向递归遍历中,上一层已经获取的元素不能再获取,要顺移一位
  3. 每个节点(含根节点)构建的 path 都是一个结果集

image.png

解题方法

依旧是回溯的模板题

复杂度

  • 时间复杂度:

添加时间复杂度, 示例: O ( n ) O(n) O(n)

  • 空间复杂度:

添加空间复杂度, 示例: O ( n ) O(n) O(n)

Code


  class Solution {

    private List<List<Integer>> result = new ArrayList<>();
    private LinkedList<Integer> path = new LinkedList<>();

    // 通过回溯,构建逻辑树,每个节点(含根节点)构建的 path 都是一个结果集
    public List<List<Integer>> subsets(int[] nums) {
        backTracking(nums, 0);
        return result;
    }

    private void backTracking(int[] nums, int startIndex) {
        // 当前节点 path 放入结果集
        result.add(new ArrayList<>(path));

        // 终止条件
        // 切割到了最后一位
        if (startIndex == nums.length) {
            return;
        }

        for (int i = startIndex; i < nums.length; i++) {
            path.add(nums[i]);

            backTracking(nums, i + 1);

            path.removeLast();
        }

        return;
    }
}

90.子集II

思路

回溯中,有两种去重方式,一种是横向遍历的去重,一种是纵向递归上的去重
这里应该采用横向遍历上的去重,也即遍历过的数不能再遍历

image.png

注意:去重时不能把当前节点也代入去重逻辑,否则会误判导致当前节点及其子树被剪掉

解题方法

描述你的解题方法

复杂度

  • 时间复杂度:

添加时间复杂度, 示例: O ( n ) O(n) O(n)

  • 空间复杂度:

添加空间复杂度, 示例: O ( n ) O(n) O(n)

Code


  class Solution {

    private List<List<Integer>> result = new ArrayList<>();
    private LinkedList<Integer> path = new LinkedList<>();

    // 回溯中,有两种去重方式,一种是横向遍历的去重,一种是纵向递归上的去重,这里应该采用横向遍历上的去重,也即遍历过的数不能
    public List<List<Integer>> subsetsWithDup(int[] nums) {

        // 排序
        Arrays.sort(nums);

        backTracking(nums, 0);
        
        return result;
    }

    private void backTracking(int[] nums, int startIndex) {
        
        // 收集每个节点的结果,包含根节点
        result.add(new ArrayList<>(path));

        // 终止条件
        if (nums.length < startIndex) {
            return;
        }

        for (int i = startIndex; i < nums.length; i++) {
            // 横向去重,如果当前节点的值和前一个节点相同,说明重复,当前节点及其所有子树都剪掉
            // 去重需要从起始位置的第二个位置开始
            if (i != startIndex && nums[i] == nums[i - 1]) {
                continue;
            }

            path.add(nums[i]);

            backTracking(nums, i + 1);

            path.removeLast();
        }

        return;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值