回溯算法(java版)

回溯算法


回溯算法模版

private void backtrack("原始参数") {
    //终止条件(递归必须要有终止条件)
    if ("终止条件") {
        //一些逻辑操作(可有可无,视情况而定)
        return;
    }

    for (int i = "for循环开始的参数"; i < "for循环结束的参数"; i++) {
        //一些逻辑操作(可有可无,视情况而定)

        //做出选择

        //递归
        backtrack("新的参数");
        //一些逻辑操作(可有可无,视情况而定)

        //撤销选择
    }
}

问题1字符串无重复选取

剑指 Offer 38. 字符串的排列

输入一个字符串,打印出该字符串中字符的所有排列。

 

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

 

示例:

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

 

限制:

1 <= s 的长度 <= 8
通过次数100,112
提交次数177,682

套入框架

class Solution {
    public String[] permutation(String s) {
        Set<String> res = new HashSet<>();
        back(s.toCharArray(),"",new boolean[s.length()],res);
        //toArray转为object类型的数组,要想转为指定类型的数组,需要我们new String[0] 注意里面的字符最大是0-res.size(),再多会补null;
        return res.toArray(new String[res.size()]);

    }
    private void back(char[] chars,String tmp,boolean[] visited,Set<String> res){
        //首先是终止条件
        if(chars.length == tmp.length()){
            //一些所需要的操作
            res.add(tmp);
            return;
        }
        for(int i=0;i<chars.length;i++){
            //排除一些选项或者跳过某些东西
            if(visited[i]) continue;
            //做出选择
            visited[i] =true;
            //回溯
            back(chars,tmp+chars[i],visited,res);
            //撤销选择
            visited[i] = false;
        }

    }
}

问题2数组无重复选取

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

 

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [1]
输出:[[1]]

 

提示:

    1 <= nums.length <= 6
    -10 <= nums[i] <= 10
    nums 中的所有整数 互不相同


套入框架

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new LinkedList<>();
        //回溯四项,第一个是原来的数据集,第二个变量为了建立1,0(表示有没有走过),1表示走过,就不再走了),
        // 然后建立一个一维的list存贮当前元素,res存储最终结果
        back(nums,new int[nums.length],new LinkedList<Integer>(),res );
        return res;
    }
    private void back(int[] nums,int[] visited,LinkedList<Integer> tmp,List<List<Integer>> res){
        //终止条件
        if(nums.length == tmp.size()){
            res.add(new LinkedList(tmp));
            return;
        }
        //开始for循环遍历
        for(int i=0;i<nums.length;i++){
            //跳过某些元素
            if(visited[i] ==1) continue;
            //做出选择
            visited[i] =1;
            //回溯
            tmp.add(nums[i]);
            back(nums,visited,tmp,res);
            //撤销选择
            visited[i] =0;
            tmp.removeLast();
        }
    }
}

问题3数组重复选取(和无重复元素选取有一定不同)

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>> res = new LinkedList<>();
        if(candidates.length ==0 ) return res;
        // 五个元素,第一个变量是原有数组,第二个变量为了保证i是一个可以重复选取的值,第三个是目标变化,第四个存放单个最终结果,最后一个变量存放最终结果
        dfs(candidates,0,target,new LinkedList<>(),res);
        return res;
    }
    public void dfs(int[] candidates,int start,int target,LinkedList<Integer> tmp,List<List<Integer>> res){
        //终止条件
        if(target<0) return;
        if(target ==0){
            res.add(new LinkedList<Integer>(tmp));
            return;
        }
        for(int i =start;i<candidates.length;i++){
            //重复元素与不重复元素的区别,重复元素,不需要做出选择和撤销选择,重复元素需要建立 1,0或者true或者false来跳过元素
            tmp.add(candidates[i]);
            dfs(candidates,i,target-candidates[i],tmp,res);
            tmp.removeLast();
        }
    }
}

总结

回溯算法还是背模版好这样可以做到事半功倍。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值