美文网首页
算法07-分治、回溯

算法07-分治、回溯

作者: 一亩三分甜 | 来源:发表于2021-02-25 00:12 被阅读0次

    《算法练习-文章汇总》

    分治:是递归的细分类,是一种特殊或较为复杂的递归

    找重复性,最近的重复性,最优的重复性
    最优重复性是动态规划
    一个大问题会由许多子问题组成
    分解问题,组合最后每个子问题的结果

    为什么是这样一种思路呢,因为程序语言只支持if else for loop recursion,变成重复性不断反复的迭代

    什么地方不需要划分成子问题的:n的阶乘n(n-1)...

    泛型递归模板

    //1.递归终止条件  terminator
    if(level > MAX_LEVEL){
    process_result
    return;
    }
    //2.处理当前层 process current logic
    process(level,param);
    //3.下探下一层 drill down 
    recur(level:level+1,newParam);
    //4.清理当前层 restore current status
    

    分治模板
    相比递归多了一步,在下探下一层和清理当前层之间增加一层,集中子结果
    process and generate the final result

    Java
    private static int divide_conquer(Problem problem, ) {    
    if (problem == NULL)//recursion terminator 
    {    int res = process_last_result();   
         return res;      
    }  
    subProblems = split_problem(problem)//处理当前逻辑
    res0 = divide_conquer(subProblems[0])//下探到下一层
    res1 = divide_conquer(subProblems[1])
    result = process_result(res0, res1);
    return result;
    }
    
    # Python
    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, ...)   subresult3 = self.divide_conquer(subproblems[2], p1, ...)   …  
    # process and generate the final result   
    result = process_result(subresult1, subresult2, subresult3, …)    
    # revert the current level states
    
    C/C++
    //C/C++
    int divide_conquer(Problem *problem, int params) 
    {  // recursion terminator  
    if (problem == nullptr) 
    {    process_result   
     return return_result;  
     }   
     // process current problem  
     subproblems = split_problem(problem, data)  
     subresult1 = divide_conquer(subproblem[0], p1)  
     subresult2 = divide_conquer(subproblem[1], p1)  
     subresult3 = divide_conquer(subproblem[2], p1)  
     ...  
     // merge  
     result = process_result(subresult1, subresult2, subresult3)  
     // revert the current level status   return 0;}
     
    JavaScript
    //Javascript
    const divide_conquer = (problem, params) => {  
    // recursion terminator  
    if (problem == null) { 
       process_result    return  
     }   
     // process current problem  
    subproblems = split_problem(problem, data)  
    subresult1 = divide_conquer(subproblem[0], p1) 
    subresult2 = divide_conquer(subproblem[1], p1)  
    subresult3 = divide_conquer(subproblem[2], p1)  
    ...  
    // merge  
    result = process_result(subresult1, subresult2,subresult3) 
    // revert the current level status
    }
    

    回溯

    回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其他的可能的分步解答再次尝试寻找问题的答案。

    回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:

    • 找到一个可能存在的正确的答案
    • 在尝试了所有可能的分步方法后,宣告该问题没有答案

    在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算。

    括号生成

    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

    示例 1:
    输入:n = 3
    输出:["((()))","(()())","(())()","()(())","()()()"]
    示例 2:
    输入:n = 1
    输出:["()"]

     private List<String> result;
    
        public List<String> generateParenthesis(int n) {
            result = new ArrayList<>();
            _generate(0,0,n,"");
            return result;
        }
    
        private void _generate(int left,int right,int n,String s){
            //递归终止条件
            if (left == n && right ==n){
                result.add(s);
                return; 
            }
            //处理当前层逻辑
            if (left < n)
                _generate(left+1, right, n, s + "(");
            if (left>right)
                _generate(left,right+1, n, s + ")");
            //下探到下一层
            //清理当前层
        }
    

    Pow(x,n)(Facebook)

    实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。
    示例 1:
    输入:x = 2.00000, n = 10
    输出:1024.00000
    示例 2:
    输入:x = 2.10000, n = 3
    输出:9.26100
    示例 3:
    输入:x = 2.00000, n = -2
    输出:0.25000
    解释:2-2 = 1/22 = 1/4 = 0.25

        public double myPow(double x, int n) {
            if (n < 0){
                x = 1/x;
            }
            return fastPow(x,n);
        }
        public double fastPow(double x,int n){
            if (n == 0)
                return 1.0;
            double result = fastPow(x,n/2);
            if (n%2==0)
                return  result * result;
            else
                return  result * result * x;
        }
    

    子集(Facebook,字节跳动,亚马逊)

    给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
    解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
    示例 1:
    输入:nums = [1,2,3]
    输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
    示例 2:
    输入:nums = [0]
    输出:[[],[0]]

        public List<List<Integer>> subsets(int[] nums) {
            List<List<Integer>> ans = new ArrayList<>();
            if(nums.length == 0)return  ans;
            dfs(ans,nums,new ArrayList<>(),0);
            return ans;
        }
        public void dfs(List<List<Integer>> ans,int[] nums,List<Integer> list,int index){
            //terminator
            if (index == nums.length){
                ans.add(new ArrayList<>(list));
                return;
            }
            //processs current logic
            dfs(ans,nums,list,index+1);// not pick the number of the index
            list.add(nums[index]);
            //drill down
            dfs(ans,nums,list,index+1);//pick the number of the index
            //reverse current state
            list.remove(list.size() -1);
        }
    

    多数元素(亚马逊、字节、Facebook)

    哈希表法

        public int majorityElement(int[] nums) {
            Map<Integer,Integer> countMaps = new HashMap<>();
            for (int num : nums) {
           countMaps.put(num,countMaps.getOrDefault(num,0)+1);
            }
            Map.Entry<Integer,Integer> maxEntry = null;
            for (Map.Entry<Integer,Integer> en:countMaps.entrySet()) {
                if (maxEntry == null||en.getValue()>maxEntry.getValue()){
                    maxEntry = en;
                }
            }
            return maxEntry.getKey();
        }
    

    大顶堆

        public int majorityElement0(int[] nums) {
            Map<Integer,Integer> countMaps = new HashMap<>();
            for (int num : nums) {
                countMaps.put(num,countMaps.getOrDefault(num,0)+1);
            }
            PriorityQueue<Map.Entry<Integer,Integer>> priorityQueue = new PriorityQueue<>(((o1, o2) -> (o2.getValue() - o1.getValue())));//大顶堆
            for (Map.Entry<Integer,Integer> en:countMaps.entrySet()) {
                priorityQueue.add(en);
            }
            return priorityQueue.poll().getKey();
        }
    

    大顶堆一次循环

        public int majorityElement1(int[] nums) {
            Map<Integer,Integer> countMaps = new HashMap<>();
            PriorityQueue<Map.Entry<Integer,Integer>> priorityQueue = new PriorityQueue<>(((o1, o2) -> (o2.getValue() - o1.getValue())));//大顶堆
            for (int num : nums) {
                countMaps.put(num,countMaps.getOrDefault(num,0)+1);
            }
            priorityQueue.addAll(countMaps.entrySet());
            return priorityQueue.poll().getKey();
        }
    

    电话号码的字母组合(亚马逊)

    public List<String> letterCombinations(String digits) {
            if (digits == null || digits.length() == 0)return new ArrayList<>();
            Map<Character,String> hashMap = new HashMap<>();
            hashMap.put('2',"abc");
            hashMap.put('3',"def");
            hashMap.put('4',"ghi");
            hashMap.put('5',"jkl");
            hashMap.put('6',"mno");
            hashMap.put('7',"pqrs");
            hashMap.put('8',"tuv");
            hashMap.put('9',"wxyz");
            List<String> list = new LinkedList<>();
            dfs("",digits,0,list,hashMap);
            return list;
        }
        public void dfs(String s,
                        String digits,
                        int level,
                        List<String>list,
                        Map<Character,String>hashMap){
            //terminator
            if (level == digits.length()){
                list.add(s);
                return;
            }
            //process current logic
            String letters = hashMap.get(digits.charAt(level));
            for (int i = 0;i < letters.length();i++){
                //drill down
                dfs(s+letters.charAt(i),digits,level+1,list,hashMap);
            }
        }
    

    N皇后
    n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
    给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
    每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
    示例 1:
    输入:n = 4
    输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
    解释:如上图所示,4 皇后问题存在两个不同的解法。
    示例 2:
    输入:n = 1
    输出:[["Q"]]

        //创建一个N行的数组,下标i对应N*N棋盘格子第i行的皇后位置
        List<List<String>> res = new ArrayList<List<String>>();
        //三个集合,分别判断某一列,左斜线(左上到右下的斜线),右斜线(左下到右上的斜线)
        Set<Integer> columns = new HashSet<Integer>();
        Set<Integer> hypotenuse1 = new HashSet<Integer>();
        Set<Integer> hypotenuse2 = new HashSet<Integer>();
        public List<List<String>> solveNQueens(int n) {
            int[] arr = new int[n];
            dfs(n,0,arr);
            return res;
        }
        private void dfs(int n,int x,int[] arr) {
            if(x==n) {
                //如果x==n说明所有的皇后都摆放完了
                //将arr数组中保存的结果拼接起来
                List<String> tmp = new ArrayList<String>();
                for(int k=0;k<n;++k) {
                    char[] row = new char[n];
                    Arrays.fill(row, '.');
                    row[arr[k]] = 'Q';
                    tmp.add(new String(row));
                }
                res.add(tmp);
                return;
            }
            //遍历一行中的每一列,并检查竖线、左斜线、右斜线是否有皇后
            for(int y=0;y<n;++y) {
                if(columns.contains(y)) {
                    continue;
                }
                if(hypotenuse1.contains(x-y)) {
                    continue;
                }
                if(hypotenuse2.contains(x+y)) {
                    continue;
                }
                //如果检查通过,设置这一行的皇后位置,并将竖线、左斜线、右斜线的值放入集合中,并继续下一行递归
                //当下一层的所有递归遍历完后,回到本轮需要将之前集合、arr数组中保存的结果都清空
                arr[x] = y;
                columns.add(y);
                hypotenuse1.add(x-y);
                hypotenuse2.add(x+y);
                dfs(n,x+1,arr);
                arr[x] = -1;
                columns.remove(y);
                hypotenuse1.remove(x-y);
                hypotenuse2.remove(x+y);
            }
        }
    

    相关文章

      网友评论

          本文标题:算法07-分治、回溯

          本文链接:https://www.haomeiwen.com/subject/zxtyfltx.html