【数据结构/leetcode】回溯算法部分代码

77.组合

题目链接

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

示例:

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

答案:

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        //存储结果
        List<List<Integer>> ansList=new ArrayList<List<Integer>>();
        //存储每次的组合
        List<Integer> list=new ArrayList<Integer>();
        backtracking(ansList,list,1,n,k);
        return ansList;
    }

    public static void backtracking(List<List<Integer>> ansList, List<Integer> list, int startInedx, int n, int k){
        if(list.size()==k){
            //这里一定要new,不然里面是空的,
            //如果是ansList.add(list); 则是把list的地址加给ansList。
            //后面要修改list的内容时,ansList中的地址指向的内容也会改,所以这里一定要创建一个新的。
            ansList.add(new ArrayList<Integer>(list));
            return;
        }

        //剪枝操作
        //k-list.size(),保证最后至少还剩的个数,放在组合里刚好够要求的个数
        for(int i=startInedx;i<=n-(k-list.size())+1;i++){
            list.add(i);
            backtracking(ansList,list,i+1,n,k);
            list.remove(list.size()-1);
        }
    }
}

216.组合总和3

题目连接

找出所有相加之和为 nk 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。

示例 1:

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

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> ansList=new ArrayList<List<Integer>>();
        List<Integer> list=new ArrayList<Integer>();
        backtracking(ansList,list,1,k,n);
        return ansList;
    }

    public static void backtracking(List<List<Integer>> ansList, List<Integer> list, int startIndex, int k, int n){
        if(list.size()==k){
            if(n==0){
                ansList.add(new ArrayList(list));
            }
            return;
        }

        //n是剩下要达到的值,i必须要小于n才行。
        for(int i=startIndex;(i<=9-(k-list.size())+1)&&i<=n;i++){
            list.add(i);
            backtracking(ansList,list,i+1,k,n-i);
            list.remove(list.size()-1);
        }
    }
}

17.电话号码字母组合:

题目链接

给定一个仅包含数字 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 {
    static String[] alphabet={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    public List<String> letterCombinations(String digits) {
        List<String> ansList=new ArrayList<String>();
        StringBuilder sb=new StringBuilder();
        backTracking(ansList,sb,digits,0);
        return ansList;
    }

    public static void backTracking(List<String> ansList, StringBuilder sb, String digits, int index){
        if(digits.equals("")){
            return;
        }
        if(sb.length()==digits.length()){
            ansList.add(new String(sb));
            return;
        }

        String str=alphabet[digits.charAt(index)-'0'-2];
        for(int i=0;i<str.length();i++){
            sb.append(str.charAt(i));
            backTracking(ansList,sb,digits,index+1);
            sb.delete(sb.length()-1,sb.length());
        }
    }
}

39.组合总和

题目连接

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500

答案:

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> ansList=new ArrayList<List<Integer>>();
        List<Integer> list=new ArrayList<Integer>();
        //这里不需要返回值,刚刚报错了
        Arrays.sort(candidates);
        backTracking(ansList, list, candidates,target,0);
        return ansList;
    }

    public void backTracking(List<List<Integer>> ansList, List<Integer> list, int[] candidates, int target, int index){
        if(target==0){
            ansList.add(new ArrayList<Integer>(list));
            return;
        }

        //i代表序号
        for(int i=index;i<candidates.length&&candidates[i]<=target;i++){
            target-=candidates[i];
            list.add(candidates[i]);
            backTracking(ansList,list,candidates,target,i);
            list.remove(list.size()-1);
            target+=candidates[i];
        }
    }
}

40.组合总和2

题目连接

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]

答案:

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> ansList=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        Arrays.sort(candidates);
        boolean[] used=new boolean[candidates.length];
        backTracking(ansList,list,candidates,target,used,0);
        return ansList;
    }

    public void backTracking(List<List<Integer>> ansList, List<Integer> list, int[] candidates, int target, boolean[] used, int startIndex){
        if(target==0){
            ansList.add(new ArrayList(list));
            return;
        }

        for(int i=startIndex;i<candidates.length&&candidates[i]<=target;i++){
            //used[i-1]==false说明是并列的两个一样的,只有在一个枝头上的才能重复
            if(i>0&&candidates[i-1]==candidates[i]&&used[i-1]==false){
                continue;
            }
            used[i]=true;
            target-=candidates[i];
            list.add(candidates[i]);
            backTracking(ansList,list,candidates,target,used,i+1);
            list.remove(list.size()-1);
            target+=candidates[i];
            used[i]=false;

        }
    }
}

131.分割回文串

题目连接

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

示例 1:

输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]
示例 2:

输入:s = “a”
输出:[[“a”]]

提示:

1 <= s.length <= 16
s 仅由小写英文字母组成

class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> ansList=new ArrayList<>();
        List<String> list=new ArrayList<>();
        backTracking(ansList,list,s,0);
        return ansList;
    }

    //startIndex:下一个字符串起始的下标
    public void backTracking(List<List<String>> ansList, List<String> list, String s, int startIndex){
        if(startIndex>=s.length()){ //切到头了
            ansList.add(new ArrayList(list));
            return;
        }

        //挨个切分,如果该str是回文串,则再切后面的,如果不是则重新切
        for(int i=startIndex;i<s.length();i++){
            String str=s.substring(startIndex,i+1);
            
            if(isPalindromic(str)){//如果是回文串
                list.add(str);
                backTracking(ansList,list,s,i+1);
                list.remove(list.size()-1);
            }

        }
    }

    //判断是否为回文串:对称位置的字符是否相等
    private boolean isPalindromic(String str){
        for(int i=0;i<str.length()-i;i++){
            if(str.charAt(i)!=(str.charAt(str.length()-1-i))){
                return false;
            }
        }
        return true;
    }
}

93.复原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”]

提示:

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

答案:

class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String> ansList=new ArrayList<>();
        if(s.length()>12||s.length()<4)
            return ansList;
        StringBuilder sb=new StringBuilder();
        backTracking(ansList, s, sb,0,0);
        return ansList;
    }

    private void backTracking(List<String> ansList, String s, StringBuilder sb, int startIndex,int time){
        if(time==3){
            String str=s.substring(startIndex,s.length()); //获取最后一段
            if(isIP(str)){
                sb.append(str);
                ansList.add(new String(sb));
                sb.delete(sb.length()-str.length(),sb.length());
                return;
            }
        }

        for(int i=startIndex;i<s.length();i++){
            String str=s.substring(startIndex,i+1);

            if(isIP(str)){//如果是IP地址
                sb.append(str);
                sb.append('.');
                time++;
                backTracking(ansList, s, sb, i+1,time);
                time--;
                //-1是因为减去点的位置
                sb.delete(sb.length()-str.length()-1,sb.length());
            }
        }
    }

    private boolean isIP(String str){
        int sum=0;
        //判断字符串为空的方法
        if(str.length()<=0||!str.equals("0")&&str.startsWith("0")){
            return false;
        }
        //这里不能直接str-'0',因为比如str-'0',字符串和字符不能相减
        for(int i=0;i<str.length();i++){
            sum=sum*10+(str.charAt(i)-'0');
            if(sum>255){
                return false;
            }
        }
        return true;
    }
}

78.子集

题目链接

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

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

输入:nums = [0]
输出:[[],[0]]

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> ansList=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        Arrays.sort(nums);
        ansList.add(new ArrayList());
        backTracking(ansList,list,nums,0);
        return ansList;
    }

    private void backTracking(List<List<Integer>> ansList, List<Integer> list, int[] nums,  int startIndex){
        //当list的长度和nums的长度一样的时候,就说明没有了
        if(list.size()==nums.length){
            return;
        }

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

491.递增子序列(有点难,不太理解)

题目链接

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是 2 。

示例:

输入:[4, 6, 7, 7]
输出:[[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

提示:

给定数组的长度不会超过15。
数组中的整数范围是 [-100,100]。
给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。

class Solution {
    public List<List<Integer>> findSubsequences(int[] nums) {
        List<List<Integer>> ansList=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        //这道题不能排序,数组是有序的	
        backTracking(ansList, list,nums,0);
        return ansList;
    }

    public void backTracking(List<List<Integer>> ansList, List<Integer> list, int[] nums,  int startIndex){
        if(list.size()>=2){
            ansList.add(new ArrayList(list));
        }
        boolean[] used=new boolean[201]; //控制每一层有无重复
        for(int i=startIndex;i<nums.length;i++){
            if(used[nums[i]+100]||(list.size()>0&&list.get(list.size()-1)>nums[i])){
                continue;
            }
            used[nums[i]+100]=true; //记录这个数值在本层已经用过了,不能在用了
            list.add(nums[i]);
            backTracking(ansList,list,nums,i+1);
            list.remove(list.size()-1);
        }
    }
}

46.全排列

题目链接

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

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

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> ansList=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        boolean[] used=new boolean[nums.length];
        backTracking(ansList,list,nums,used);
        return ansList;
    }

    private void backTracking(List<List<Integer>> ansList, List<Integer> list, int[] nums,  boolean[] used){
        if(list.size()==nums.length){
            ansList.add(new ArrayList(list));
            return;
        }

        for(int i=0;i<nums.length;i++){
            if(!used[i]){ //如果nums[i]在这个枝丫上没有用过
                used[i]=true;
                list.add(nums[i]);
                backTracking(ansList,list,nums,used);
                list.remove(list.size()-1);
                used[i]=false;
            }
        }
    }
}

47.全排列2

关于树层和树枝上的去重

题目链接

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

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

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

提示:

1 <= nums.length <= 8
-10 <= nums[i] <= 10

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> ansList=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        boolean[] used=new boolean[nums.length];
        Arrays.sort(nums);
        backTracking(ansList,list,nums,used);
        return ansList;
    }

        private void backTracking(List<List<Integer>> ansList, List<Integer> list, int[] nums,  boolean[] used){
        if(list.size()==nums.length){
            ansList.add(new ArrayList(list));
            return;
        }
		
        //roUsed用于在每一层排除重复。
        //used用于在每一个树枝上排除重复
        boolean[] roUsed=new boolean[21];
        for(int i=0;i<nums.length;i++){
            if(!used[i]){ //如果nums[i]在这个枝丫上没有用过
                if(roUsed[nums[i]+10]){
                    continue;
                }
                roUsed[nums[i]+10]=true;
                used[i]=true;
                list.add(nums[i]);
                backTracking(ansList,list,nums,used);
                list.remove(list.size()-1);
                used[i]=false;
            }
        }
    }
}

上面那种比较直观,或者这样写(这样写似乎内存消耗更多?):

   private void backTracking(List<List<Integer>> ansList, List<Integer> list, int[] nums,  boolean[] used){
        if(list.size()==nums.length){
            ansList.add(new ArrayList(list));
            return;
        }

        for(int i=0;i<nums.length;i++){
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false)
                //说明这一层已经用过了,因为used[i]=false;
                continue;
            if(!used[i]){ //如果nums[i]在这个枝丫上没有用过
                used[i]=true;
                list.add(nums[i]);
                backTracking(ansList,list,nums,used);
                list.remove(list.size()-1);
                used[i]=false;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值