回溯算法2

电话号码的字母组合

题目

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
力扣链接

思路

返回输入数字字符串对应的字母组合。
字母组合的字母数就等于输入的数字的字符数。
我们先将数字与字母的对应关系,写成一个字符串数组,方便下面操作。

String[] strings = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

采用回溯的思路,把结构想象成一棵树,那么取第一个数字对用的字符是在第一层,取第二个就在第二层,以此类推。
反映在代码中就是在第几层for循环中。
那么我们通过把数字字符串转换成整型,找到对应的字符串,然后进行组合。

代码
List<String> result = new ArrayList<>();
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0){
            return result;
        }
        String[] strings = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        backtracking(digits, strings, 0);
        return result;
    }
    
    StringBuilder stringBuilder = new StringBuilder();
    
    public void backtracking(String digits, String[] strings, int num) {
        if (num == digits.length()) {
            result.add(stringBuilder.toString());
            return;
        }
        String str = strings[digits.charAt(num) - '0']; // 数字对应的字符串
        for (int i = 0; i < str.length(); i++) {
            stringBuilder.append(str.charAt(i));
            backtracking(digits, strings, num + 1);
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        }
    }

切割回文子串

思路

回溯三部曲:

  • 递归的参数值
    一维数组 path, 用来放已经回文的子串。这里需要经过判断。
    需要startIndex, 因为切割是不能重复的,需要startIndex来让深层的for循环,移动切割位置。
  • 终止条件
    一串字符,切割到最后一位的时候终止。
  • 单层循环逻辑
    单层循环的时候需要判断是不是回文子串,不是回文子串就直接跳过一次for循环,该不是回文子串的内容也就不会加入到path中。
  • 判断回文子串
    写一个for循环,判断开头和末尾是否相等即可。
代码
List<List<String>> res = new ArrayList<>();
    Deque<String> path = new LinkedList<>();
    public List<List<String>> partiton(String s) {
        backtracking(s, 0);
        return res;
    }
    public void backtracking(String s, int StartIndex) {
        if (StartIndex >= s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = StartIndex; i < s.length(); i++) {
            if (isPalindrome(s, StartIndex, i)) {
                String str = s.substring(StartIndex, i + 1);
                path.addLast(str);
            }else {
                continue;
            }
            backtracking(s, i + 1);
            path.removeLast();
        }
    }
    public boolean isPalindrome(String s, int StartIndex, int end) {
        for (int i = StartIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }

复原IP地址

题目

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效的 IP 地址。

思路
  • 递归参数
    分割IP地址,不能重复分割,所以需要startIndex
    同时还需要一个参数pointNum记录加入的逗点数量。
public void 
  • 递归终止条件
    对于IP地址,切割四次就是有三个逗点的时候说明应该结束了。此时还需要判断一下第四段是否合法,如果合法的话,就加入结果集。
  • 单层搜索逻辑
    使用startIndex区分切割,然后剪枝掉无效的分支。

子集问题

问题 Leet78

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

代码
public class subsetsClass {
    static List<List<Integer>> result = new ArrayList<>();
    static Deque<Integer> path = new LinkedList<>();
    public static List<List<Integer>> subsets(int[] nums) {
        if (nums.length == 0) {
            result.add(new ArrayList<>());
            return result;
        }
        backTracking(nums, 0);
        return result;
    }

    public static void backTracking(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]);
            backTracking(nums, i + 1);
            path.removeLast();
        }
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 2};
        List<List<Integer>> res = new ArrayList<>();
        res = subsets(nums);
        System.out.println(res);
    }
}

子集问题II

问题 Leet90

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

思路

这道题与78的区别在于,给的集合中有重复的元素,但是解集要求不能有重复的子集。
去重的思路就是使用一个used数组,表名哪一个元素取过了。通过这个标签,就可以得到不重复的子集。

代码
public class subsetsWithDupClass {
    List<List<Integer>> result = new ArrayList<>();
    Deque<Integer> path = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if (nums.length == 0) {
            result.add(new ArrayList<>());
            return result;
        }
        Arrays.sort(nums); //去重要记得排序
        used = new boolean[nums.length];
        Helper(nums, 0);
        return result;
    }

    public void Helper(int[] nums, int startIndex) {
        result.add(new ArrayList<>(path));
        if (startIndex >= nums.length) {
            return;
        }
        for (int i = startIndex; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1] && !used[i-1]) { //与前一项相等且前一项的used是1的说明是同一层的,要去重
                continue;
            }
            path.add(nums[i]);
            used[i] = true;
            Helper(nums, i + 1);
            used[i] = false;
            path.removeLast();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写代码的信哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值