思路

解决recursion的问题就像是证明if and only if一样,我们得从两个方向分别想。

  • 第一我们要正着想,如何把现有规模为N的问题split成N - 1的问题。这里头一般有两种思路,第一种是我们不断从N decrement到N-1,第二种是我们不断从0 increment,直到N。

  • 第二我们要反着想,如何用N-1的问题表示回来N的问题,也就是假设N-1的问题已经被一个黑盒解决了,我们如何利用这个黑盒来解决N的问题。

所以你可以看到我们每次call recursion的时候其实表示的是N的状态,只不过借用了N- 1这个黑盒。这也有点像动态规划,我们都需要一个状态转移方程来表示N与N-1的关系。

//calculate the integer sum of all digit characters in a string. 
public int helper(String s){
         if (s == null || s.length() == 0){
             return 0;
         }
         if (Character.isDigit(s.charAt(0))){
             return (s.charAt(0) - '0') + helper(s.substring(1));
         }else{
             return helper(s.substring(1));
         }
     }
//double each char in a string 
public String helper(String s){
         if (s == null || s.length() == 0){
             return "";
         }
         return  Character.toString(s.charAt(0)) + Character.toString(s.charAt(0)) + helper(s.substring(1));
     }
//calculate the power of n, assuming that k is positive
//从这些关于recursion的加减乘除我们可以看到,对于求sum的,一般我们通过减法来缩小规模,比如integer sum of digit character,那里头不断减少string的长度,所以数据减小的速度是线性的。而这里计算power和greatest common divisor,因为本身是做乘法,所以我们可以用除法减小数据规模,达到 O(logn)级别的时间。

public int power(int n, int k){
         if (n == 0){
             return 0;
         }
         if (k == 0){
             return 1;
         }
         return n * power(n, k - 1);
     }
//here is an extended version of pow(x, n), where n could be positive of negative, and the time complexity is O(logn)
public double myPow(double x, int n) {
        if (n == 0){
            return 1;
        }
        if (n == 1){
            return x;
        }
        if (n == -1){
            return 1/x;
        }
        if (n > 0){
            if (n % 2 == 0){
                return myPow(x, n/2)* myPow(x, n/2);
            }
            else{
                return x * myPow(x, n/2)* myPow(x, n/2);
            }
        }
        else{
            if (n % 2 == 0){
                return myPow(x, n/2)* myPow(x, n/2);
            }
            else{
                return 1/x * myPow(x, n/2)* myPow(x, n/2);
            }
        }
    }
    //calculate the greatest common divisor between two numbers
     public int helper(int a, int b){
         return findGCD(a, b, 1);
     }
     public int findGCD(int a, int b, int divisor){
         if (a/divisor < 1 || b/divisor < 1){
             return 1;
         }
        
         if (divisible(a, b, divisor)){
             if (divisor == 1){
                 return findGCD(a, b, divisor + 1);
             }else{
                 return divisor * findGCD(a/divisor, b/divisor, divisor);
             }
         }else{
                return findGCD(a, b, divisor + 1);
         }
     }
     public boolean divisible(int a, int b, int divisor){
         int temp1 = a/divisor;
         int temp2 = b/divisor;
         return (temp1*divisor == a) && (temp2*divisor == b);
     }

分治思想是recursion的一种体现

Divide and Conquer是recursion思想的一个分支。比如之前我们算sum的时候是线性从头到尾减小数据规模的,而使用分治的思想我们是从中间劈开一半,对数级别减小数据规模的。一般使用分治方法的prerequisite是分开的左右两部分应该是互相独立没有dependency on each other的,但是我们在parallel programming上学到了可以parallel 计算prefix sum,which is originally hard to divide and conquer due to dependency. 这里我们暂且不需要讨论。我们一般认为只要左右两部分没有dependency,那么一般就可以分治,当然这里头更大的前提是可以用recursion解决。毕竟分治是recursion的branch。

     public int helper(int[] array){
         return findSum(array, 0, array.length - 1);
     }
     public int findSum(int[] array, int start, int end){
         if (start >= end){
             return array[start];
         }
         int mid = start + (end - start)/2;
         int left = findSum(array, start, mid);
         int right = findSum(array, mid + 1, end);
         
         return left + right;
     }
     public int helper(int[] array){
         return findMax(array, 0, array.length - 1);
     }
     public int findMax(int[] array, int start, int end){
         if (start >= end){
             return array[start];
         }
         int mid = start + (end - start)/2;
         int left = findMax(array, start, mid);
         int right = findMax(array, mid + 1, end);
         
         return Math.max(left, right);
     }

liut2的算法笔记
0 声望1 粉丝

« 上一篇
Count and Say

引用和评论

0 条评论