leetcode--分治和回溯--基础概念总结和经典题目分析汇总

目录

一:分治和回溯概念总结

1.1 分治和回溯定义:

1.2 分治模板

题目一:子集

题目二:电话号码的字母组合

题目三:长度为n的开学字符串中字典序第k小的字符串


一:分治和回溯概念总结

1.1 分治和回溯定义:

分治:就是把大问题分解成重复的小问题。

回溯:采用试错的思想,它尝试分步去解决一个问题,在分布解决问题的过程中,

          当它发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,

          再通过其它可能的分步再次尝试寻找问题的答案

1.2 分治模板

def divide_conquer(problem,param1,param2,...)
    #recursion terminator
    if problem is None:
        print_result
        return
    # prepare data
    data = prepare_data(problem)
    subproblems = split_problem(problem,data)
    # conquer subproblems
    subresult1 = self.divide_conquer(subproblems[0],p1,...)
    subresult2 = self.divide_conquer(subproblems[1],p1,...)
    ....
    # process and generate the final result
    result = process_result(subresult1,subresult2,...)

题目一:子集

(题目链接:https://leetcode-cn.com/problems/subsets/)

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

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

示例 1:

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

思路:明显使用分治,以上面例子为例:初始化一个列表,该列表有三个位置,

三个位置可以放nums中对应元素,也可以不放,把三个位置走完,我们可能的结果就出来了。

如何实现可以放元素和不放元素-->先放该对应元素,继续调用递归,然后把该元素删除,继续递归

class Solution {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    List<Integer> temp = new ArrayList<Integer>();
    public List<List<Integer>> subsets(int[] nums) {
        recur(nums,0,new ArrayList<Integer>());
        return res;
    }
    public List<List<Integer>>  recur(int[] nums, int level, List<Integer> curr){
        if(level == nums.length){
            res.add(new ArrayList<Integer>(curr));
            return res;
        }
        curr.add(nums[level]);
        recur(nums,level+1,curr);
        curr.remove(curr.size()-1);
        recur(nums,level+1,curr);
        return res;
    }
}

第二次写的代码:

class Solution {
    List<Integer> temp = new ArrayList<Integer>();
    List<List<Integer>> res = new ArrayList<List<Integer>>(); 
    public List<List<Integer>> subsets(int[] nums) {
        dfs(0,nums);
        return res;
    }
    public void dfs(int cur, int[] nums){
        if(cur == nums.length){
            List<Integer> temp2 = new ArrayList<Integer>(temp);
            res.add(temp2);
            return;
        }
        temp.add(nums[cur]);
        dfs(cur+1,nums);
        temp.remove(temp.size()-1);
        dfs(cur+1,nums);
    }
}

题目二:电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]

思路:还是以上述样例来阐述我们的解题思路:首先字符串“23”是两位,相当于我们有两个位置需要放置东西,

每个位置怎么放:“23”的第一位是字符2,所以可以放置字符2对应的字符串“abc”中对应的某一个,那么这个该如何放?

我们只有先把“abc”怎么放的问题解决掉才能去解决外面的字符3对应的位置怎么放。

于是对于“abc”怎么放:我们采用一个for循环,每次放一个,例如放a,然后调用递归,去放外面字符3对应的位置。调用递归之后,

把刚刚放进去的a取出来,接着放b,接着递归,就形成了这种for循环里面调用递归的方式。

核心代码:

for(int i =0;i<curr_s.length();i++){
            temp.append(curr_s.charAt(i));
            dfs(digits,level+1,temp,map1);
            temp.deleteCharAt(level);
 }

class Solution {
    List<String> res = new ArrayList<String>();
    public List<String> letterCombinations(String digits) {
        Map<Character,String> map1 = new HashMap<Character,String>();
        map1.put('2',"abc");
        map1.put('3',"def");
        map1.put('4',"ghi");
        map1.put('5',"jkl");
        map1.put('6',"mno");
        map1.put('7',"pqrs");
        map1.put('8',"tuv");
        map1.put('9',"wxyz");
        dfs(digits,0, new StringBuffer(),map1);
        return res;
    }
    public List<String> dfs(String digits,int level,StringBuffer temp,Map<Character,String> map1) {
        if(digits.length() == 0){
            return res;
        }
        if(level == digits.length()){
            //res.add(new String(temp.toString()));
            res.add((temp.toString()));
            return res;
        }
        char digit = digits.charAt(level);
        String curr_s = map1.get(digit);
        for(int i =0;i<curr_s.length();i++){
            temp.append(curr_s.charAt(i));
            dfs(digits,level+1,temp,map1);
            temp.deleteCharAt(level);
        }
        return res;
    }

}

题目三:长度为n的开学字符串中字典序第k小的字符串

(题目链接:https://leetcode-cn.com/problems/the-k-th-lexicographical-string-of-all-happy-strings-of-length-n/

一个 「开心字符串」定义为:

仅包含小写字母 ['a', 'b', 'c'].
对所有在 1 到 s.length - 1 之间的 i ,满足 s[i] != s[i + 1] (字符串的下标从 1 开始)。
比方说,字符串 "abc","ac","b" 和 "abcbabcbcb" 都是开心字符串,但是 "aa","baa" 和 "ababbc" 都不是开心字符串。

给你两个整数 n 和 k ,你需要将长度为 n 的所有开心字符串按字典序排序。

请你返回排序后的第 k 个开心字符串,如果长度为 n 的开心字符串少于 k 个,那么请你返回 空字符串 。

示例 1:

输入:n = 1, k = 3
输出:"c"
解释:列表 ["a", "b", "c"] 包含了所有长度为 1 的开心字符串。按照字典序排序后第三个字符串为 "c" 。

思路:定义一个“abc”的字符串每次可以从“abc”中取元素,最后当取到的元素形成的字符串长度到达条件的时候递归结束。

方法一:此处是把所有长度为n的字符串找到再进行过滤

class Solution {
    List<String> result1 = new ArrayList<String>();

    public  String getHappyString(int n, int k) {
        String s = "abc";
        backtrack(s,0,n,new StringBuffer());
        if(result1.size()>=k){
            return result1.get(k-1);
        }
        return "";
        //System.out.println(result1);
        
    }
    public void backtrack(String s, int level, int n, StringBuffer curr){
        if(level == n){
            boolean flag = true;
            if(curr.length()==1 ){
                result1.add(new String(curr.toString()));
            }else if(curr.length()>1){
                
                for(int m=0;m<curr.length()-1;m++){
                    if(curr.charAt(m)==curr.charAt(m+1)){
                        flag = false;
                    }
                }
                if(flag){
                result1.add(new String(curr.toString()));
            }
            }
            return;
        }
        for(int i =0;i<s.length();i++){
            curr.append(s.charAt(i));
            backtrack(s,level+1,n,curr);
            curr.deleteCharAt(curr.length()-1);
        }
    }
}

方法二:在生成字符串的时候就只产生哪些符合条件的字符串

class Solution {
    public String getHappyString(int n, int k) {
        String s1 = "abc";
        List<String> res = new ArrayList<String>();
        backtrack(s1,n,new StringBuffer(),res);
        if(res.size()>=k){
            return res.get(k-1);
        }else{
            return "";
        }

    }
    public void backtrack(String s1, int n,StringBuffer curr,List<String> res){
        if(curr.length() == n){
            res.add(new String(curr.toString()));
            return;
        }
        for(int i =0;i<s1.length();i++){
            if(curr.length() == 0){
                curr.append(s1.charAt(i));
                backtrack(s1,n,curr,res);
                curr.deleteCharAt(curr.length()-1);
            }
            else if(curr.length()>0){
                if(curr.charAt(curr.length()-1) != s1.charAt(i)){
                    curr.append(s1.charAt(i));
                    backtrack(s1,n,curr,res);
                    curr.deleteCharAt(curr.length()-1);
                }
            }
        }
        
    }
}

猜你喜欢

转载自blog.csdn.net/yezonghui/article/details/112968830