有这么一种感觉,递归算法在思考角度上是“显得很懒惰的”,为什么这么说呢?

因为递归的本质是自己调用自己,机器这么做很干净利落,一步一步来就好,只要把每一次递归的条件改动一下就好。

但是我们人脑在思考递归时就显得很吃力……(因为在硬件的底层实现,递归是很恐怖的,要不断的开新栈,然后再逐个返回收栈,可能是我脑子的缓存不够用吧,多想那么几个复杂的就崩了)

不过,要写出一个递归函数还是很容易的。

递归函数的要义在于,每一次执行的操作是一样的,不同的是每一次的初始条件,所以与其说自己调用自己,不如理解成每次调用一个一摸一样的函数。

然后,有这个想法开始,就可以往下继续像,如果这么一直调用下去,什么时候停止呢?所以就需要有一个终止的条件。

在之后,调用一个函数是希望得到某一个结果的,那么这个return的结果就需要写出来,包括最底层的return和每一层的return。

换一种思维来考虑递归

比如最常见斐波那契数列,我们正常的思维是从零到一,而递归的实现,是“实现1需要的是0”,所以正好是反过来的。所以这样来想,对于像我这种顺序思考的大脑来说,确实是很偷懒的。

一个实现的例子

例子来自于PTA的“最大子列和”问题,其中的一种方法是“分而治之”(和归并排序的感觉很像,都是递归嘛),第一眼看上去有点吓人,不过逻辑理顺,然后一步步实现就好了。

/* 使用函数调用的方法 */
#include <cstdio>
/* 分而治之的方法 */
/* 递归的方法 */
int max3 (int A, int B, int C)
{
    return A > B ? A > C ? A : C : B > C ? B : C;
}
int divideLeftRight(int listNum[], int left, int right)
{
    int maxLeft, maxRight;
    int maxLeftBorder, maxRightBorder;
    int tempLeft, tempRight;
    //处理递归结束
    if (left == right) {
        if (listNum[left] > 0) return listNum[left];
        else return 0;
    }
    //处理左右“分”的过程
    int mid = (left + right) / 2;
    maxLeft = divideLeftRight(listNum, left, mid);
    maxRight = divideLeftRight(listNum, mid + 1, right);

    //处理跨越分界的情况
    //意思是最大的子列不在左半边也不在右半边,所以要考虑把两边加起来看看
    tempLeft = 0;
    maxLeftBorder = 0;
    for (int i = mid; i >= 0; i--) {
        tempLeft += listNum[i];
        if (tempLeft > maxLeftBorder)
            maxLeftBorder = tempLeft;
    }
    tempRight = 0;
    maxRightBorder = 0;
    for (int i = mid + 1; i <= right; i++) {
        tempRight += listNum[i];
        if (tempRight > maxRightBorder)
            maxRightBorder = tempRight;
    }
    return max3(maxLeft, maxRight, maxLeftBorder + maxRightBorder);
}
int maxSubseqSum(int A[], int n)
{
   return divideLeftRight(A, 0, n - 1);
}
int main()
{
    int N;
    int num[100001] = {0};
    scanf("%d", &N);
    for (int i = 0; i < N; i++) {
        scanf("%d", &num[i]);
    }
    printf("%d", maxSubseqSum(num, N));
    return 0;

}