面试总结------回溯问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a940902940902/article/details/87926646

回溯问题作为面试算法中经典问题之一,同时也是很容易总结出一套固定解题模板的算法类别,这里使用Leetcode中top-100 liked 为例,并尝试在解题过程中总结出对应的解题思路和解题模板

重点:
1.对于回溯问题最重要的一点就是在 foward—>backtrack 这一过程状态的变化 例如从一个状态i 向前进行若干操作之后回溯到状态i 此时要把对应的其他改变的值恢复到状态i
2.对于回溯使用的场景大多是穷尽所有的情况 同时又不易通过遍历将情况一一穷举出来的时候。

示例1

Letter Combinations of a Phone Number
Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.
Input: "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
class Solution {
   
    List<String> result=new ArrayList<>();
    Map<String, String> phone = new HashMap<String, String>() {{
        put("2", "abc");
        put("3", "def");
        put("4", "ghi");
        put("5", "jkl");
        put("6", "mno");
        put("7", "pqrs");
        put("8", "tuv");
        put("9", "wxyz");
  }};
    public List<String> letterCombinations(String digits) {
        if(digits.length()==0){
            return result;
        }
        backTrack(digits,0,"");
        return result;
        
        
    
    }
    
    public void backTrack(String digits,int i,String cur){
        if(i==digits.length()){
            result.add(cur);
            return;
        }
        String tmp=phone.get(digits.substring(i,i+1));
        for(int j=0;j<tmp.length();j++){
            cur=cur+tmp.substring(j,j+1);
            backTrack(digits,i+1,cur);
            cur=cur.substring(0,cur.length()-1);
        }       
    }
}

这里刨除本题特有的 例如map部分不看 可以得到一个此类题的解题框架
首先如果是要返回一个list 可以定义全局变量 List<所需类型> result=new ArrayList<>(); 用来保存最终的结果
然后定义一个backTrack函数
public void backTrack(String digits ,int index,String cur) // 对于输出list类型的题 backTrack返回值类型为void ,第一个参数设定为输入的要进行遍历的数据,第二个参数设定为遍历到原始数据digits的第几个位置 一般使用index记录forward的深度 同时利用index作为返回的判断依据 ,第三个参数作为forward为 index 深度时临时得到的结果 ,之后继续forward 可以在此结果的基础上进行操作

public void backTrack(String digits,int index,String cur){
// 终止条件判断
if(index==digits.length()){
result.add(cur);//将当前的中间状态作为最后的终止状态
return;
}
String tmp=phone.get(digits.substring(i,i+1));
for(int j=0;j<tmp.length();j++){
cur=cur+tmp.substring(j,j+1); //修改中间状态
backTrack(digits,i+1,cur); //forward
cur=cur.substring(0,cur.length()-1);// 将中间状态修改回forward之前的状态
}
}

示例二

Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

下面可以使用上述总结的方法进行实践
和上述问题略有不同的是 修改中间状态到forward之前的状态不用把( 去掉 而是直接在其后增加)

class Solution {
    List<String> result=new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        backTrack("",0,0,n);
        return result;
                        
    }
    public void backTrack(String cur,int left,int right,int n){
        if(cur.length()==2*n){
            result.add(cur);
            return;
        }
        if(left<n){
            backTrack(cur+"(",left+1,right,n);
        }
        if(right<left){
            backTrack(cur+')',left,right+1,n);
        }
    }
   
}

示例三
全排列问题 有重复数字的全排列和无重复数字的全排列
全排列问题也是一个典型的回溯问题 该问题解题的关键点就是如何在每一轮全排列的时候把已经放入到list中的数据不再进行排列,对于没有重复数字的情况而言,可以通过查看list是否contains该数字 有就跳过 ,对于有重复数字的全排列情况而言 可以通过一个boolean数组来保存每一个数字被选择的情况

class Solution {
    
    private List<List<Integer>> result=new ArrayList<>();
    public List<List<Integer>> permute(int[] nums) {
       if(nums.length==0){
           return result;
       }
        ArrayList<Integer> tmp=new ArrayList<>(); 
        backTrack(nums,tmp);
        return result;
    }
    public void backTrack(int[]nums,ArrayList<Integer> tmp){
        if(tmp.size()==nums.length){
            result.add(new ArrayList<>(tmp));
            return;
        }
        for(int i=0;i<nums.length;i++){
            boolean isContain=false;
            if(tmp.contains(nums[i])){
                isContain=true;
            }
            if(!isContain){
                tmp.add(nums[i]);
                backTrack(nums,tmp);
                tmp.remove(tmp.size()-1);
            }
        }
    }
   
}

#有重复的全排列 
class Solution {
    
    List<List<Integer>> result=new ArrayList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
       ArrayList<Integer> tmp=new ArrayList<>();
        boolean[] isUsed=new boolean[nums.length];
        Arrays.sort(nums);
       backTrack(nums,isUsed,tmp);
        
        return result;
    }
    public void backTrack(int[] nums,boolean[] isUsed,ArrayList<Integer> tmp){
        if(tmp.size()==nums.length){
            result.add(new ArrayList<>(tmp));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(isUsed[i]==true){
                continue;
            }
            if(i>0&&nums[i]==nums[i-1]&&!isUsed[i-1]){  # 这句话是关键可以保证有序数组不会以完全相同的状态出现两次
                continue;
            }
            tmp.add(nums[i]);
            isUsed[i]=true;
            backTrack(nums,isUsed,tmp);
            tmp.remove(tmp.size()-1);
            isUsed[i]=false;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/a940902940902/article/details/87926646
今日推荐