算法通关村第18关【白银】| 回溯热门问题

文章探讨了回溯算法在解决组合总和、分割回文串、子集选择、排列问题以及字母大小写全排列和单词搜索等场景的应用,通过递归和回溯终止条件实现解空间探索。
摘要由CSDN通过智能技术生成

1.组合总和问题

思路:回溯模板

(1)确定方法返回值和参数

分析可知遍历数组然后求和值,不需要返回什么值

参数也就是candidates,list,path,target,start

(2)确定回溯终止条件

当满足求和的时候终结

(3)确定单层逻辑

判断当前是否满足条件是就添加进条件,然后返回

不是就继续向下递归,这里需要注意从i开始,也就是可以取自己

当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的值

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        List<Integer> path = new ArrayList<Integer>();
        trace(list,path,candidates,target,0);
        return list;

    }
    public void trace(List list,List path,int[] candidates,int target,int start){ 
        if(target<0){
            return;
        }
        if(target == 0){
            list.add(new ArrayList<>(path));
            return;
        }
        for(int i = start;i<candidates.length;i++){
            path.add(candidates[i]);
            trace(list,path,candidates,target - candidates[i],i);
            path.remove(path.size()-1);
        }
    }
}

2.分割回文串

思路: 

(1)确定方法返回值和参数

分析可知遍历字符串进行分割,不需要返回什么值

参数也就是s,list,path,start

(2)确定回溯终止条件

当遍历完字符串就结束一次递归

(3)确定单层逻辑

判断当前分割方案是否是回文串不是继续往后纳入字母

是就继续向下递归,这里需要注意从i+1开始,也就是不可以取自己

当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的方案

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

    }
    public boolean isPalindrom(String s,int l,int r){
        for(int i = l,j = r;i<j;i++,j--){
            if(s.charAt(i) != s.charAt(j)){
                return false;
            }
        }
        return true;
    }
    public void trace(String c,int start,List list,List path){
        if(start >= c.length()){
            list.add(new ArrayList<String>(path));
            return;
        }
        for(int i = start;i<c.length();i++){
            if(!isPalindrom(c,start,i)){
                continue;
            }
            String str = c.substring(start,i + 1);
            path.add(str);
            trace(c,i+1,list,path);
            path.remove(path.size()-1);
        }

    }
}

3.子集问题

 思路:子集问题不需要剪枝,遍历全部并且每次都加添加进结果集

(1)确定方法返回值和参数

分析可知遍历数组,不需要返回什么值

参数也就是nums,list,path,start

(2)确定回溯终止条件

当遍历完数组就结束一次递归

(3)确定单层逻辑

将当前数字添加进path,继续向下递归,这里需要注意从i+1开始,也就是不可以取自己

当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的数字

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

    public void trace(List list,List path,int[] nums,int start){
        if(start > nums.length){
            return;
        }
        list.add(new ArrayList<>(path));
        for(int i = start;i<nums.length;i++){
            path.add(nums[i]);
            trace(list,path,nums,i+1);
            path.remove(path.size()-1);
        }
    }
}

4.排列问题

思路:

(1)确定方法返回值和参数

分析可知遍历数组,不需要返回什么值

参数也就是nums,list,path

(2)确定回溯终止条件

当遍历完数组就结束一次递归

(3)确定单层逻辑

注意这里的for循环从0开始,代表可以重复取。

判断当前数字是否已经在path里面了,是的话就跳过当前数字

否则将当前数字添加进path,继续向下递归,这里需要注意从i+1开始,也就是不可以取自己

当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的数字

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

    public void trace(List list,List path,int[] nums){
        if(path.size()==nums.length){
            list.add(new ArrayList<Integer>(path));
            return;
        }
        for(int i = 0;i<nums.length;i++){
            if(used[i]){
                continue;
            }
            path.add(nums[i]);
            used[i] = true;
            trace(list,path,nums,i+1);
            path.remove(path.size()-1);
            used[i] = false;
        }
    }
}

5.字母大小写全排列

思路:回溯模板

深度:s.length()-数字

宽度:2(小写、大写)

跳过数字,将字母大小写转换,然后进行递归,触底加入结果集。

返回进行回溯,转换大小写,继续递归。

class Solution {
    public List<String> letterCasePermutation(String s) {
         List<String> list = new ArrayList<>();
         trace(list,s.toCharArray(),0);
         return list;
    }

    public void trace(List list,char[] s,int start){
        while(start<s.length&&Character.isDigit(s[start])){
            start++;
        }
        if(start == s.length){
            list.add(new String(s));
            return;
        }
        s[start] ^= 32;
        trace(list,s,start+1);
        s[start] ^=32;
        trace(list,s,start+1);
    }
}

6.单词搜索

思路:

(1)确定方法返回值和参数

分析可知遍需要返回boolean,参数自然需要board、i、j、word、len、cur

(2)确定回溯终止条件

当碰到不符合的字母就返回false代表这一条支路不行,word长度和递归深度一致时代表这条支路可以返回true

(3)确定单层逻辑

从当前的i,j出发,上下左右四个方向可以选择,只要是没用访问过的点和边界内的就进行递归,并且标记当前的点为已使用,补上参数visited

当递归返回的时候需要进行回溯,也就是清除已使用的标记

class Solution {
    public boolean exist(char[][] board, String word) {
        int h = board.length, w = board[0].length;
        boolean[][] visited = new boolean[h][w];
        char[] s = word.toCharArray();
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                boolean flag = trace(board,visited,i,j,word.length(),1,s);
                if(flag){
                    return true;
                }
            }
        }
        return false;
    }
    public boolean trace(char[][] board,boolean[][] visited,int i,int j,int len,int cur,char[] word){
        if (board[i][j] != word[cur-1]) {
            return false;
        } 
        if(len == cur){
            return true;
        }
        visited[i][j] = true;
        boolean a=false,b=false,c=false,d=false;
        if(i+1<board.length&&visited[i+1][j]==false) {
             a = trace(board,visited,i+1,j,len,cur+1,word);
        }
           
        if(i-1>=0&&visited[i-1][j]==false){
            b = trace(board,visited,i-1,j,len,cur+1,word);
        } 
            
        if(j+1<board[0].length&&visited[i][j+1]==false) {
            c = trace(board,visited,i,j+1,len,cur+1,word);
        }
        if(j-1>=0&&visited[i][j-1]==false) {
            d = trace(board,visited,i,j-1,len,cur+1,word);
        }
         visited[i][j] = false;
        return a|b|c|d;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值