回溯算法fsdafasd

这里写目录标题

在这里插入图片描述
在这里插入图片描述
将题目转化为递归树的问题。
在这里插入图片描述
在这里插入图片描述
用一个List装载回溯的值,当一个回溯到达底层时候,用一个List去装载回溯的值,每次重新回溯的时候都要注意,当你重新回溯时候,一定一定要把List中值取消掉,也就是说回溯算法需要取消掉自己的操作。

1.步骤,用一个List<List> res装载返回值
用一个List跟着节点去遍历,遍历成功后,将路径值加进去,当到底的时候,也就是list值等于数组长度时候,到底了。到底就记得要回溯
回溯和递归回去有一个本质区别‘
就像月光宝盒一样,你必须撤除自己做过的操作。’

在这里插入图片描述
在这里插入图片描述
回溯算法的本质是题目让我们找全所有解法。
在这里插入图片描述
在这里插入图片描述
每次传递数值时候把数组传递进去就行了,然后控制一下start和end
记住比自己小的值不能传递进去。
每次传递这个数组就行了

本质就是递归加遍历,在每一层数组中多次调用递归,多次进行遍历。

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        //前面的数不能大于后面的数
        //用一个List<List<Integer>>返回数
        int[] num = new int[n];
        for(int i = 0;i < n;i++){
            num[i] = i + 1;
        }
        List<List<Integer>> res = new ArrayList<>();
        //用来存数值
        LinkedList<Integer> path = new LinkedList<>();//返回中间值
        for(int i = 0;i < num.length;i++){//从这里就是多重递归,开始递归之旅
            dfs(num,res,i,path,k);
            path.pollLast();

        }
        
        return res;


    }

    //不需要返回值,每次都传递res和path就行

    public void dfs(int[] num,List<List<Integer>> res,int start,LinkedList<Integer> path,int k){
        path.add(num[start]);//进了循环就将数据加入路径
        //我们用一个对象传递数据,所以不用担心返回值问题
        //返回终止条件
        if(path.size() == k){
            //路径值大小等于K返回
            res.add(new LinkedList(path));//将path的值放进去,不能放地址,这步相当于复制了path的值
            return;
        }

        for(int i = start;i < num.length - 1;i++){
            //回溯算法是递归和循环相结合
        
            dfs(num,res,i + 1,path,k);
            path.pollLast();//把尾巴弹出来撤销自己上次的选择
        }
    }
}

我这个方法,可以做,但是很蠢逼,相当于
在主函数里搞了个for循环遍历
在dfs子函数里也搞了个for循环遍历,全都是因为一个问题
那就是在一进dfs时候,就把当前节点加入路径了,这样
这个for循环就影响不到节点加入路径这个问题了。
正确降低代码量的方式应该是,将path.add()这个操作放到for循环里去,这样第一次操作时候{1,2,3,4}整个数组都会被作为选择。

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        //前面的数不能大于后面的数
        //用一个List<List<Integer>>返回数
        int[] num = new int[n];
        for(int i = 0;i < n;i++){
            num[i] = i + 1;
        }
        List<List<Integer>> res = new ArrayList<>();
        //用来存数值
        LinkedList<Integer> path = new LinkedList<>();//返回中间值
        
            dfs(num,res,0,path,k);
            


        
        return res;


    }

    //不需要返回值,每次都传递res和path就行

    public void dfs(int[] num,List<List<Integer>> res,int start,LinkedList<Integer> path,int k){
        
        //我们用一个对象传递数据,所以不用担心返回值问题
        //返回终止条件
        if(path.size() == k){
            //路径值大小等于K返回
            res.add(new LinkedList(path));//将path的值放进去,不能放地址,这步相当于复制了path的值
            return;
        }

        for(int i = start;i < num.length;i++){
            //回溯算法是递归和循环相结合
            path.add(num[i]);//进了循环就将数据加入路径
        
            dfs(num,res,i + 1,path,k);
            path.pollLast();//把尾巴弹出来撤销自己上次的选择
        }
    }
}

//一道很标准的回溯题目
在这里插入图片描述
实际上关键点就是两个
1.截取字符串之后判断劫走的部分是不是回文?是的话加入路径
2.从剩下的部分中继续劫走,当遇到空字符串时候全部路径返回
3.两个函数,一个用来回溯,另一个用来判断是不是回文字符串。

22

标准回溯算法
在这里插入图片描述
在这里插入图片描述
n = ?实际上就是告诉了你左边和右边括号数量
两个数组一个放左括号,一个放右边括号,然后去递归

当做括号数量小于等于右括号时候,向左递归,减少左括号数量是正确的
当右括号数量少于左括号时候,这个递归丧失任何意义。
//因为无法匹配

class Solution {
    public List<String> generateParenthesis(int n) {
        //这种题目必然回溯算法
        //用一个栈存储路径,当路径错误时候直接返回
        //当左边n不为n-1 不为零时候,可以一直向左递归
        int left = n,right = n;
        //
        List<String> res = new ArrayList<>();
        //作为返回值
        String path = new String();//直接用String,根本不用回溯
        //路径进行拍徘徊

        dfs(res,path,left,right);
        return res;





        

    }

    public void dfs(List<String> res,String path, int left,int right ){
        if(left == 0 && right == 0){
            //括号全部被耗尽
            res.add(path);//将字符串存进去
            return;
            
        }

        

       //无论如何都可以向左递归
       if(left - 1 >= 0){
           //只要下面的值比0大就可以往左回溯
           //左边为0都没关系,还有右边呢
              
                dfs(res,path + '(',left - 1,right);
                //递归回来之后记得恢复状态
               
       }
   
    


//如果减去1之后大于等于左边
         if(right - 1 >= left && right - 1 >= 0){
            //可以向右递归
            
                dfs(res,path + ')',left,right - 1);
                //递归回来之后记得恢复状态
                
                //将string转化为StringBuilder

        }





    }
}

直接用STring根本不用回溯。

17

回溯题目
在这里插入图片描述
在这里插入图片描述
依然可以转化为类似这种无数路径的题目
在这里插入图片描述

class Solution {
    public List<String> letterCombinations(String digits) {

        //我们从这里只能采用分割的方式拿到数据,分割的话拿到的都是数字的字符
        //所以我们必须在创建对应的表的时候,使用字符对应字符串方式,存储
        List<String> result = new ArrayList<>();//用于返回结果
        if(digits.length() == 0){
            return result;//为0则直接返回
        }

        Map<Character,String> letterMap = new HashMap<>(){{
            put('2', "abc");
            put('3', "def");
            put('4', "ghi");
            put('5', "jkl");
            put('6', "mno");
            put('7', "pqrs");
            put('8', "tuv");
            put('9', "wxyz");
           }};
           //将字符对应的映射放在这里

           backtrack(digits,letterMap,result,0,new StringBuffer());
           //需要一个index索引随时表示我们应该处理哪一个digits下标准
           return result;



    }


    public static void backtrack(String digits,Map<Character,String> letterMap,List<String> result,int index,StringBuffer path){

        if(path.length() == digits.length()){
            //到达标准了
            result.add(path.toString());
            //将字符串放进去
            return;

        }

//StringBuffer的delete功能
        //如果没到的话,随和是
        String letter = letterMap.get(digits.charAt(index));//获取那个字符串
        //然后开启递归模式
        for(int i = 0;i < letter.length();i++){
            //StringBuffer是可以进行回溯的
            path.append(letter.charAt(i));//加上这个字符串路径中
           backtrack(digits,letterMap,result,index + 1,path);//接着去进行递归
           //递归回来后,Index还是那个东西,我们值需要将path的数据进行回溯就行了
           path.deleteCharAt(path.length() - 1);//完成回溯
           //这里不能用i因为i比较大
        }

        

    }
}


90

在这里插入图片描述
这个题目讲了回溯的本质
在这里插入图片描述
可以看出来同一层一旦出现连续相同数字,就会让整体开始重复,下一层重复是没关系的
毕竟1,2,2也是我们需要的答案
最重要的就是同一层重复的,只能访问一次。
那么对Nums进行排序,将所有都放在一起
在递归的每一层都去记录这层这个数据有没有被访问过,重要的是切断每一层之间的联系。同一层之间不要访问,相同的元素。但是下一层可以访问相同元素。

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {

        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> numList = new ArrayList<>();

        dfs(nums,res, numList, 0);

        return res;
    }

    private void dfs(int[] nums,List<List<Integer>> res,List<Integer> numList,int k){

        res.add(new ArrayList(numList));

        Map<Integer,Boolean> visited = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            visited.put(nums[i], false);
        }

        for (int i = k; i < nums.length; i++) {
            if(!visited.get(nums[i])){
                numList.add(nums[i]);
                dfs(nums, res, numList,i+1);
                numList.remove(numList.size()-1);
                visited.put(nums[i], true);
            }
        }
        
    }
}

作者:LeegouHai
链接:https://leetcode-cn.com/problems/subsets-ii/solution/tu-jie-chao-xiang-xi-fen-xi-hui-su-fa-ja-h4ry/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

38

关键点是,我们必须标记一下,被访问过的元素,然后每次都是从头开始遍历,遇到访问过的元素,必须错过去,让循环继续,才能将所有没有访问过的元素都集中起来。

class Solution {
     public String[] permutation(String s) {

        HashSet<String> set = new HashSet<>();
        char[] c = s.toCharArray();//进行访问

        //需要一个数组证明z已经被访问过了
        boolean[] vis = new boolean[10];//证明已经访问过了
        StringBuilder sb = new StringBuilder();
        Arrays.fill(vis,false);
        dfs(set,c,sb,vis);

        //将set拿出来当成数组
        String[]  result = new String[set.size()];

        int idx = 0;//用来往里面放
        for(String str : set){
            result[idx++] = str;
        }

        return result;
    }


    //传递进来一个StringBuilder,传进来一个字符串数组,传递进来一个set,一个访问数组
    public void dfs(HashSet<String> set, char[]  c, StringBuilder s, boolean[] vis){

        //返回的情况是
        if(s.length() == c.length){
            set.add(s.toString());//就给变成String
            return;//直接返回了
        }

        //如果没存满,接着遍历
        for(int i = 0;i < c.length;i++){
            if(!vis[i]){
                s.append(c[i]);//直接加进来
                //随后标记一下i
                vis[i] = true;//标记为已经读了
                //不然就接着往下走
                dfs(set,c,s,vis);
                s.deleteCharAt(s.length() - 1);//移除自己最后一位
                vis[i] = false;//移除自己所有的操作


            }
            else{

                //关键就是这一步
                //我们每次都是从头开始遍历的,访问没有访问过的元素,所以遇到已经被访问过的元素
                //你必须过掉,让循环可以走进下一个,让循环可以访问下一个元素
                continue;//如果当前这个已经被访问过了,你必须去看后面那个,当前这个就过去了
            }

        }

    }
}

https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/solution/gong-shui-san-xie-tong-yong-shi-xian-qu-4jbkj/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值