递归

什么是递归,简单点,就是当一个函数用它自己来定义时就称为递归。

举个 最简单的例子:

int f(int x){
    if(x==0)
        return 0;
    else 
        return 2*f(x-1)+x*x;//递归调用 
}

当编写递归,关键是要牢记递归的四条基本法则

1. 基准情形。必须总有某些基准情形,它无需递归就能解出。

2. 不断推进。对于那些需要递归求解的情形,每一次递归调用都必须要使得解状况朝近基准情形的方向推进。

3. 设计法则。假设所有的递归调用都能运行。

4. 合成效益法则。在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。

前面三条原则是保证一个递归调用正确运行,而第四条则是保证递归能够高效运行。

再来个简单的例子:

斐波那契数列,{F_0} = 1,{F_1} = 1,{F_2} = 2,{F_i} = {F_{i - 1}} + {F_{i - 2}}

int fibo(int n){
    if(n==0 || n==1)return 1;
    else return fibo(n-1)+fibo(n-2);
}

此时编写的递归函数就不符合递归的第四条法则,会产生严重的资源消耗。

 

虽然递归程序存在很大的资源开销,但是递归程序可以简化算法设计,而且有助于给出更加简洁的代码,所以仍然不失为一种好的思想方法。

下面通过几个例子来说明递归在算法设计中的益处。

一。最大子序列和问题。输入-2, 11, -4, 13,-5, –2.答案为20(从第2个到第4个的子序列)下面几种程序设计方法。

1.一个简单的解决方案;

int  MaxSubsequenceSum(const int array[], int n){
    int thisSum, maxSum, i, j, k;
    maxSum=0;
    for(i=0;i<n;++i){
        for(j=i;j<n;++j){
            thisSum=0;
            for(k=i;k<=j;++k){
                thisSum+=array[k];
            }
            if(thisSum>maxSum){
                maxSum=thisSum;
            }
        }
    } 
    return maxSum;
}

此时我们分析知道该算法的时间复杂度为:O({n^3}).

2.对前一种算法的小改进。

int  MaxSubsequenceSum(const int array[], int n){
    int thisSum, maxSum, i, j, k;
    maxSum=0;
    for(i=0;i<n;++i){
        thisSum=0;
        for(j=i;j<n;++j){
            thisSum+=array[j];
            if(thisSum>maxSum){
                maxSum=thisSum;
            }
        }
    } 
    return maxSum;
}

3.使用递归进行程序设计。

最大子序列和可能在三处出现,或者出现在整个输入数据的左半部,或者整个出现在右半部,或者跨越输入数据的中部从而占据左右两部分,前两种情况可以递归求解,第三种情况的最大和可以通过求出前半部分的最大和(包含前半部分最后一项)以及后半部分的最大和(包含后半部分的第一项)而得到,然后将这两个和加在一起。

int MaxSubSum(const int array[], int left, int right){
    int maxLeftSum, maxRightSum;
    int maxLeftBorderSum, maxRightBorderSum;
    int leftBorderSum, rightBorderSum;
    int center, i;
    //基准情况 
    if(left==right){
        if(array[left]>0)return array[left];
        else return 0;
    }
    center=(left+right)/2;
    maxLeftSum=MaxSubSum(array, left, center);
    maxRightSum=MaxSubSum(array, center+1, right);
    
    maxLeftBorderSum=leftBorderSum=0;
    for(i=center;i>=left;--i){
        leftBorderSum+=array[i];
        if(leftBorderSum>maxLeftBorderSum)
            maxLeftBorderSum=leftBorderSum;
    }
    maxRightBorderSum=rightBorderSum=0;
    for(i=center+1;i<=right;++i){
        rightBorderSum+=array[i];
        if(rightBorderSum>maxRightBorderSum){
            maxRightBorderSum=rightBorderSum;
        }
    }
    return max(maxLeftSum, maxRightSum, maxLeftBorderSum+maxRightBorderSum);//返回三个数的最大值 
} 

int MaxSubsequenceSum(const int array[], int n){
    return MaxSubSum(array, 0, n-1);
}

算法时间复杂度为O(N\log N).

4.更为有效的方法,时间复杂度为O(N)

int  MaxSubsequenceSum(const int array[], int n){
    int thisSum, maxSum, i;
    thisSum=maxSum=0; 
    maxSum=0;
    for(i=0;i<n;++i){
        thisSum+=array[i];
        if(thisSum>maxSum)
            maxSum=thisSum;
        else if(thisSum<0)
            thisSum=0;
    } 
    return maxSum;
}

二. 高效率的取幂运算(计算{x^N}的时间复杂度仅为O({\log _2}N))。

long pow(long x, int n){
    if(n==0)return 1;
    if(n%2==0)
        return pow(x*x, n/2);
    else return pow(x*x, n/2)*x;
}

当然今天记录的只是递归的一些简单应用,在数据结构树,图等数据结构中有广泛的应用,以后将逐步学习。

posted @ 2014-07-29 22:40  Qinsli_cg  阅读(267)  评论(0)    收藏  举报