思路
解决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);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。