93.复原IP地址 78.子集 90.子集II

93.复原IP地址 78.子集 90.子集II

93.复原IP地址

力扣题目链接(opens new window)

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “[email protected]” 是 无效的 IP 地址。

示例 1:

  • 输入:s = “25525511135”
  • 输出:[“255.255.11.135”,“255.255.111.35”]

示例 2:

  • 输入:s = “0000”
  • 输出:[“0.0.0.0”]

示例 3:

  • 输入:s = “1111”
  • 输出:[“1.1.1.1”]

示例 4:

  • 输入:s = “010010”
  • 输出:[“0.10.0.10”,“0.100.1.0”]

示例 5:

  • 输入:s = “101023”
  • 输出:[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“101.0.2.3”]

提示:

  • 0 <= s.length <= 3000
  • s 仅由数字组成

思路:回溯法

题目对字符串做分割,找出某种符合条件的组合。相当于遍历一个集合,找出符合条件的组合,可使用回溯法
本题要求字符串分成4段。终止条件可判断分隔符的个数,也可判断递归的深度
当对字符串切割,如果切割的字符串,有效则stringBuilder.append
当递归到第四层时,需要判断stringBuilder是否包含全部字符串字符。因为stringBuilder可能不包含全部的字符串,举例【101023】
1.0.10.2 和 1.0.10.23 第四个元素2和23都是有效的字符,但第一种漏掉了一个元素3,不符合题意,因此要在中止条件做判断并过滤。

此题从一个集合中求组合,那么在for循环for(int i = startIndex;i

集合中元素不允许重复使用,那么每次递归的下一层就是从i+1开始的

回溯法三要素
1.方法入参和返回值。入参:String s,int startIndex 返回值:void
2.中止条件 当遍历层数达到第四层时,判断stringBuilder是否包含全部字符串字符,并中止递归。
3.核心逻辑
递归循环中截取字符串。在for循环for(int i = startIndex;i 每次切割的字符串范围为【startIndex,i】,然后判断字符串是否为有效IP地址的组成字符串
是有效的字符串,加入stringBuilder.否则break此循环

代码如下

// 时间复杂度O(4 * 2^n) n表示字符串字符个数
// 空间复杂度o(n)
static List<String> result = new ArrayList<>();
static StringBuilder stringBuilder = new StringBuilder();
static int depth = 0;

public static List<String> restoreIpAddresses(String s) {
    backTracking(s, 0);
    return result;
}

public static void backTracking(String s, int startIndex) {
    if (depth >= 4) {
        if (stringBuilder.length() - 4 == s.length()) {
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            result.add(stringBuilder.toString());
        }
        return;
    }


    for (int i = startIndex; i < s.length(); i++) {
        String str = s.substring(startIndex, i + 1);
        if (!isVaild(str)) {
            break;
        }

        stringBuilder.append(str).append(".");
        depth++;
        backTracking(s, i + 1);
        depth--;
        stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        stringBuilder.delete(stringBuilder.lastIndexOf(".") + 1, stringBuilder.length());
    }

}

public static boolean isVaild(String str) {
    if (str.length() > 1 && str.charAt(0) == '0')
        return false;
    if (str.length() >= 4)
        return false;
    if (Integer.parseInt(str) > 255 || Integer.parseInt(str) < 0)
        return false;
    for (int i = 0; i < str.length(); i++) {
        if (str.charAt(i) > '9' || str.charAt(i) < '0')
            return false;
    }
    return true;
}

78.子集

力扣题目链接(opens new window)

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]

思路:回溯

此题跟之前回溯题目区别在于
之前题目在中止条件将【子集】加入结果集,本题目每一个子集都是结果,在每一层递归将子集加入结果集
求整数数组的所有子集,子集不能重复。可以用求组合的套路来解
因为子集不能重复,所以for循环就要从startIndex开始
因为元素不能重复多次选择,所以递归的startIndex入参不能等于i,等于i+1
递归法三要素
1.方法入参和返回值。方法入参:int[] nums,int startIndex
2.中止条件。不需要中止条件,当到达一条树枝的终点后,startIndex的值会等于nums.length,不会走for循环,相当于中止条件
3.核心逻辑
递归循环中截取数字。在for循环for(int i = startIndex;i 取出集合中i对应的数字加并放入path,path加入结果集。

递归的startIndex入参不能等于i,等于i+1

将path中最后一个集合去掉即回溯

代码如下

// 时间复杂度O(n * 2^n) n表示字符串字符个数
// 空间复杂度o(n)
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();

public List<List<Integer>> subsets(int[] nums) {
    result.add(new ArrayList<>());
    if (nums == null || nums.length == 0) {
        return result;
    }
    backTracking(nums, 0);
    return result;
}

public void backTracking(int[] nums, int startIndex) {


    for (int i = startIndex; i < nums.length; i++) {
        path.add(nums[i]);
        result.add(new ArrayList<>(path));
        backTracking(nums, i + 1);
        path.remove(path.size() - 1);
    }
}

90.子集II

力扣题目链接(opens new window)

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

  • 输入: [1,2,2]
  • 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]

思路:回溯

思路:回溯
数组中存在重复元素,求数组所有不重复的子集
去重子集最好在遍历过程中,可避免超时
去重步骤
1.先对数组排序
2.for循环遍历时,比较当前结点值和上个结点值,若相同,则跳过此结点

代码如下

//时间复杂度: O(n * 2^n)
//空间复杂度: O(n)
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();

public List<List<Integer>> subsetsWithDup(int[] nums) {
    Arrays.sort(nums);
    backTracking(nums, 0);
    return result;
}

public void backTracking(int[] nums, int startIndex) {

    result.add(new ArrayList<>(path));
    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.remove(path.size() - 1);
    }
}

你可能感兴趣的:(leetcode,排序算法,java)