【leetcode-回溯】复原IP地址/子集/组合/电话号码的字母组合/二进制手表

复原IP地址

给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 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 地址。

示例 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”]

回溯法

class Solution {
    private char[] charList;
    private List<String> result = new ArrayList<String>();
    private int[] segments = new int[4];
    private int len = 0;

    public List<String> restoreIpAddresses(String s) {
        charList = s.toCharArray();
        len = charList.length;
        backtrack(0, 0);
        return result;
    }

    private void backtrack(int start, int n) {
        if (n == 4) {
            if (start == len) {
                StringBuilder sb = new StringBuilder();
                sb.append(segments[0]);
                for (int i = 1; i < 4; i++) 
                    sb.append(".").append(segments[i]);
                result.add(sb.toString());
            }
            return;
        }
        if (start == len)
            return;

        if (charList[start] == '0') {
            segments[n] = 0;
            backtrack(start + 1, n + 1);
        } else {
            int num = 0;
            for (int i = start; i < len; i++) {
                num = 10 * num + (charList[i] - '0');
                if (num > 0 && num < 256) {
                    segments[n] = num;
                    backtrack(i + 1, n + 1);
                } else {
                    break;
                }
            }
        }
    }
}

子集1

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

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

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

迭代法

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> results = new ArrayList<>();
        results.add(new ArrayList<Integer>());
        for (int n : nums) {
            int size = results.size();
            for (int i = 0; i < size; i++) {
                List<Integer> cur = new ArrayList<>(results.get(i));
                cur.add(n);
                results.add(cur);
            }
        }
        return results;
    }
}

回溯法

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> results = new ArrayList<>();
        backtrack(results, new ArrayList<Integer>(), 0, nums);
        return results;
    }

    private void backtrack(List<List<Integer>> results, List<Integer> subset, int start, int[] nums) {
        results.add(new ArrayList<>(subset));
        for (int i = start; i < nums.length; i++) {
            subset.add(nums[i]);
            backtrack(results, subset, i + 1, nums);
            subset.remove(subset.size() - 1);
        }
    }
}

子集2

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

示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

回溯法

class Solution {
    private int len;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        this.len = nums.length;
        Arrays.sort(nums);

        List<List<Integer>> result = new ArrayList<>();
        backtrack(nums, result, new ArrayList<>(), 0);
        return result;
    }

    private void backtrack(int[] ns, List<List<Integer>> res, List<Integer> comb, int start) {
        res.add(new ArrayList<>(comb));

        for (int i = start; i < len; i++) {
            if (i > start && ns[i] == ns[i - 1])
                continue;
            comb.add(ns[i]);
            backtrack(ns, res, comb, i + 1);
            comb.remove(comb.size() - 1);
        }
    }
}

迭代法

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        result.add(new ArrayList<>());
        Arrays.sort(nums);

        int start = 1;
        for (int i = 0; i < nums.length; i++) {
            List<List<Integer>> cur_result = new ArrayList<>();
            for (int j = 0; j < result.size(); j++) {
                if (j < start && i > 0 && nums[i] == nums[i - 1])
                    continue;
                List<Integer> combine = new ArrayList<>(result.get(j));
                combine.add(nums[i]);
                cur_result.add(combine);
            }
            start = result.size();
            result.addAll(cur_result);
        }
        return result;
    }
}

组合

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

回溯法

class Solution {
    private int count = 0;
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> results = new ArrayList<>();
        backtrack(results, new ArrayList<Integer>(), 1, n, k);
        return results;
    }

    private void backtrack(List<List<Integer>> results, List<Integer> subset, int start, int n, int k) {
        if (subset.size() + n - start + 1 < k)
            return;

        if (subset.size() == k) {
            results.add(new ArrayList<>(subset));
            return;
        }
        
        
        for (int i = start; i <= n; i++) {
            subset.add(i);
            backtrack(results, subset, i + 1, n, k);
            subset.remove(subset.size() - 1);
        }
    }
}

电话号码的字母组合

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

示例 1:
输入:digits = “23”
输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]

示例 2:
输入:digits = “”
输出:[]

示例 3:
输入:digits = “2”
输出:[“a”,“b”,“c”]

回溯法

在这里插入图片描述

class Solution {
    public List<String> letterCombinations(String digits) {
        List<String> result = new ArrayList<>();
        if (digits.length() < 1) {
            return result;
        }

        String[] letterMap = { "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        backtrack(result, letterMap, digits, 0, new StringBuffer());
        return result;
    }

    private void backtrack(List<String> result, String[] letterMap, String digits, int index, StringBuffer curString) {
        if (index == digits.length()) {
            result.add(curString.toString());
        } else {
            char curLetter = digits.charAt(index);
            String letters = letterMap[curLetter - '2'];
            for (int i = 0; i < letters.length(); i++) {
                char letter = letters.charAt(i);
                curString.append(letter);
                backtrack(result, letterMap, digits, index+1, curString);
                curString.deleteCharAt(index);
            }
        }
    }
}

二进制手表

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。

例如,下面的二进制手表读取 “3:25” 。
在这里插入图片描述

给你一个整数 turnedOn ,表示当前亮着的 LED 的数量,返回二进制手表可以表示的所有可能时间。你可以 按任意顺序 返回答案。

小时不会以零开头:

  • 例如,“01:00” 是无效的时间,正确的写法应该是 “1:00” 。

分钟必须由两位数组成,可能会以零开头:

  • 例如,“10:2” 是无效的时间,正确的写法应该是 “10:02” 。

示例 1:
输入:turnedOn = 1
输出:[“0:01”,“0:02”,“0:04”,“0:08”,“0:16”,“0:32”,“1:00”,“2:00”,“4:00”,“8:00”]

示例 2:
输入:turnedOn = 9
输出:[]

回溯法

class Solution {
    private boolean[] used = new boolean[10];
    public List<String> readBinaryWatch(int turnedOn) {
        List<String> result = new ArrayList<>();
        if (turnedOn > 8 || turnedOn < 0)
            return result;

        backtrack(result, 0, 0, turnedOn);
        return result;
    }

    private void backtrack(List<String> result, int start, int cur, int num) {
        int minute = 0x3f & cur;
        int hour = 0xf & (cur >> 6);
        if (minute > 59 || hour > 11)
            return;
        if (num == 0) {
            StringBuilder sb = new StringBuilder();
            sb.append(hour).append(':');
            if (minute < 10) 
                sb.append('0');
            sb.append(minute);
            result.add(sb.toString());
            return;
        } 

        for (int i = start; i < 10; i++) {
            if (used[i])
                continue;
            used[i] = true;
            backtrack(result, i + 1, cur | (1 << i), num - 1);
            used[i] = false;
        }
    }
}

哈希表存储+组合

class Solution {
    public List<String> readBinaryWatch(int turnedOn) {
        List<String> result = new ArrayList<>();
        if (turnedOn > 8 || turnedOn < 0)
            return result;

        Map<Integer, List<Integer>> minuteMap = new HashMap<>();
        Map<Integer, List<Integer>> hourMap = new HashMap<>();
        for (int i = 0; i < 60; i++) {
            minuteMap.put(i, new ArrayList<Integer>());
            if (i < 12)
                hourMap.put(i, new ArrayList<Integer>());
        }
        for (int i = 0; i < 60; i++) {
            int count = Integer.bitCount(i);
            minuteMap.get(count).add(i);
            if (i < 12)
                hourMap.get(count).add(i);
        }

        for (int i = 0; i <= turnedOn; i++) {
            List<Integer> minutes = minuteMap.get(turnedOn - i), hours = hourMap.get(i);
            for (int h : hours) {
                for (int m : minutes) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(h).append(':');
                    if (m < 10)
                        sb.append('0');
                    sb.append(m);
                    result.add(sb.toString());
                }
            }
        }
        return result;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值