zoukankan      html  css  js  c++  java
  • 算法思维方式—— 由排列组合想到的

    最近算法题也刷了不少,小有感悟。

    我觉得刷题时一般的思维方式是迭代思维。就是我们老是想着通过循环,通过顺序解决下一个来迭代解决整个问题。

    典型事例有:2Sum, 3Sum, 排序问题,以及用双指针或快慢指针法解决的问题。

    迭代思维是一种很直接的思维方式,但绝不简单,因为找到正确的循环方式并不是一件容易的事情。

    但有些问题用迭代思维方式是很难解决的,或者说这些问题本身就不适合用循环来解。

    比如求组合数问题。C(n, 2)还能用2层循环来解,但C(n, m)呢?用迭代就很难求解了,不自然。这是用递归思维方式却很自然。

    先取一个,把问题化为C(n, m-1), 再取一个,把问题化为C(n, m-2), 如此递归即可。

        public static int combinationNum = 0;
        public static void combine(int[] target, int[] result, int st, int index,int m){
            if (m == 0){
                combinationNum++;
                for (int i : result){
                    System.out.printf(i + " ");
                }
                System.out.println();
                System.out.println(combinationNum);
            }else {
                for (int i = index; i < target.length - m + 1; i++) {
                    result[st] = target[i];
                    combine(target, result, st+1, i+1,m-1);
                }
            }
        }
    

      求排列数也是如此,把A(n,n) 化为A(n-1,n-1),直至化为A(1,1)。

        public static void swap(int[] result, int st, int ele){
            int tmp = result[st];
            result[st] = result[ele];
            result[ele] = tmp;
        }
    
        //轮番把各个元素放在第一位,然后递归求解。注意复位。
        public static void arrange(int[] result, int st){
            if (st == result.length-1){
                for (int i : result){
                    System.out.printf(i + " ");
                }
                System.out.println();
            }else {
                for (int i = st; i < result.length; i++) {
                    swap(result, i, st);
                    arrange(result, st+1);
                    swap(result,i , st);
                }
            }
        }
    

      对于这类型的问题用递归的感觉就是干净,简洁,有一种逻辑的美感。

    不过把问题递归化并不是一件容易的事。有一种常用的技巧是“一子动天下”。就是针对一个元素的有无进行分类讨论,这个元素常常是最后一个。这时往往可以把问题二分递归化。

    上面两个例子实际上也是针对一个元素进行讨论,不过它们是把问题多分化,所以外层有循环来遍历。下面给出一个二分递归化的例子。

    给出一个数组,里面是不重复的int 数字。 求满足和为S的所有数字组合。(数组[2, 3, 6, 7],  和为 7, 结果为:[ [7], [2, 2, 3] ] )。也就是换硬币问题。

    这时我们可以针对最后一个元素是否包含,把问题二分化。然后递归遍历整个解法空间:

        private List<List<Integer>> result = new ArrayList<>();
    
        //一子动天下的典型事例,递归思维方式的典型。dfs遍历整个解法空间,自然得出结果。干净,简洁。
        public void combinations(int[] candidates, int target, List<Integer> combination, int limit){
            if (limit < 0 ){
                return;
            }else if (target == 0){
                result.add(combination);
            }else {
                if (target - candidates[limit] >= 0){
                    combination.add(candidates[limit]);
                    combinations(candidates, target - candidates[limit], new ArrayList<>(combination), limit);
                    combination.remove(combination.size()-1);
                }
                combinations(candidates, target, combination, limit-1);
            }
        }
    
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            Arrays.sort(candidates);
            int limit = candidates.length-1;
            combinations(candidates, target, new ArrayList<Integer>(), limit);
            return result;
        }
    

      

    算法问题多变,复杂。如果迭代不行,不妨试试递归方式。“一子动天下”是一种较好的讨论方式。

  • 相关阅读:
    ExternalInterface.addCallback()方法,as2 和 as3的区别
    关于table的display问题
    关于table的display问题
    js表单验证是否空值的简单处理办法
    《那些年啊,那些事——一个程序员的奋斗史》——112
    《那些年啊,那些事——一个程序员的奋斗史》——111
    《那些年啊,那些事——一个程序员的奋斗史》——113
    《那些年啊,那些事——一个程序员的奋斗史》——115
    《那些年啊,那些事——一个程序员的奋斗史》——112
    《那些年啊,那些事——一个程序员的奋斗史》——115
  • 原文地址:https://www.cnblogs.com/zqiguoshang/p/7374597.html
Copyright © 2011-2022 走看看