如何理解递归?

一、为什么难理解?

这个问题说简单也简单的,说复杂,当你想写出一个棒的递归却又写不出来,为什么会形成这种局面,是技术不够高?我猜测这是一种思维在作怪,我们看待递归的时候,一遇到内部递归就去想又要调用递归了,堆栈又怎么怎么了,我们习惯以计算机的思维去搞。其实根本不用考虑这点,我说的是根本,网上最爱举的例子就是斐波那契数列个人觉得这个例子也不错,我也举点例子,再分析该例子。

二、例子走走

例1:这样的一个数组,从上到下,类似于贪心法,但只能走化斜下方向,算出从上到下的总的值,可以不用给出路径。

int g_Array[6][6] = {
 { 9 },
 { 2,6 },
 { 4,5,6 },
 { 8,2,5,7 },
 { 9,3,6,2,4} ,
 { 2,5,3,7,6,8 } };

解:按题意,我们直接可以观察出 9->6->6->7->6->7,那么我们怎么搞它转化成递归求解呢?这个就要用贼眉鼠眼来操作了。。分析这个流程,有总值,当前值,位置等等概念,位置还可以动,好的,我们就从位置入手,位置就是我们要递归的参数,总值就是要我们返回的内容,好我们可以写出一个大概。

int Path(int r,int c)
{
    int curValue=g_Array[r][c];

    return curValue+Path(r+1,c);//以当前值来递归,c不确定。
}

这个总值怎么来呢?在数学上分析,就是每一项的值加起来,再递归调用下一项,这不就是总值了吗?根据我所分析的,很容易就写出个类似的代码,哈,就如我前面说的,是不是不用去思考堆栈怎么调用嘛。然后我们就要这个Path的退出情况,当调用行数超过6行的时候,或者列超过6行的时候,或者小于0行的时候,就可以返回了。

int Path(int r , int c)
{
    if (r<0||r > 5 ||c<0 ||c>5)
        return 0;
    int curValue = g_Array[r][c];

    int nTemp=(g_Array[r + 1][c + 1] > g_Array[r + 1][c - 1] ? Path(r + 1,c + 1) : Path(r + 1, c - 1));
    //如果下一项的前一个与下一项的后一个相比较。递归的位置调用!
    return  curValue +nTemp;//当前值递归调用
    //类似于数学中f(all)=f(0)+f(1)+f(2)...
}

例2:一道二叉树的题型,源于LintCode上,打印出所有根到叶的结点?打印出根到叶等于5的。

      1
     / \     2   4
   / \   2   1
     / \     1   2
就是这颗树。

解:可能马上想到二叉树的先序遍历,没有问题我们可以写出一个先序遍历,但要分析问题,像上例一样,哪些在变化,节点肯定在变化,当前结点的总值变化,当前已存的点位在变化,我们可以把这些在变化的写成参数,大概我们可以出这样的代码

void ForEach(TreeNode *pNode,int target,vector<int> vec)
{//注此处的vector要使用副本,即进入第一个分支,都有一个前一个vec副本,当本次操作后,不影响前面的vec。
    if (pNode->left)
        ForEach(pNode->left, target - pNode->val, vec);
     if(pNode->right)
        ForEach(pNode->right, target - pNode->val, vec);
}

再考虑临界条件,哪种情况退出它?就要像上面的目光短来看事情,当遇到叶子结点,就来判断,如果一直减到了0,就证明刚刚好,满足要求的路径,即可以把该路径上的东西添加到vector中,好的思路也算不错了,我们大可以写出类似的代码了

void ForEach(TreeNode *pNode, int target, vector<int> vec)
{
    if (!pNode->right && !pNode->left)//叶结点了
    {
        if (target - pNode->val == 0)//如果减到了0,就刚刚好
        {
            vec.push_back(pNode->val);
            g_vec.push_back(vec);//整个路径添加到vec中。
        }
        return;
    }
    vec.push_back(pNode->val);

    if (pNode->left)
        ForEach(pNode->left, target - pNode->val, vec);
    if (pNode->right)
        ForEach(pNode->right, target - pNode->val, vec);
}

三、小例完整源码

小例1源码:
顺便打印出路径

#include <iostream>
using namespace std;

int g_Array[6][6] = {
                    { 9 },
                    { 2,6 },
                    { 4,5,6 },
                    { 8,2,5,7 },
                    { 9,3,6,2,4 } ,
                    { 2,5,3,7,6,8 } };

int Path(int r , int c)
{
    if (r<0||r > 5 ||c<0 ||c>5)
        return 0;

    int curValue = g_Array[r][c];
    cout << curValue << "->";
    int nTemp=(g_Array[r + 1][c + 1] > g_Array[r + 1][c - 1] ? Path(r + 1,c + 1) : Path(r + 1, c - 1));

    return  curValue +nTemp;//当前值加下一个值。
}



int main(int argc, char*argv[])
{
    cout<<  "总计:" <<Path(0,0)<<endl;
    return 0;
}

运行示例:
《如何理解递归?》

小例2源码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;


/* 1 / \ 2 4 / \ 2 1 / \ 1 2 */

vector<vector<int>> g_vec;

class TreeNode {
        public:
        int val;
        TreeNode *left, *right;
        TreeNode(int val) {
            this->val = val;
            this->left = this->right = NULL;
        }
};

void ForEach(TreeNode *pNode, int target, vector<int> vec)
{
    if (!pNode->right && !pNode->left)//末端了
    {
        if (target - pNode->val == 0)
        {
            vec.push_back(pNode->val);
            g_vec.push_back(vec);
        }
        return;
    }
    vec.push_back(pNode->val);

    if (pNode->left)
        ForEach(pNode->left, target - pNode->val, vec);
    if (pNode->right)
        ForEach(pNode->right, target - pNode->val, vec);
}

vector<vector<int>> binaryTreePathSum(TreeNode *root, int target) {
    vector<int> vec;
    ForEach(root,target, vec);
    return g_vec; 
}
int main()
{
    TreeNode root(1);
    TreeNode c_l(2);
    TreeNode c_r(4);
    TreeNode c2_l(2);
    TreeNode c2_r(1);
    TreeNode c3_l(1);
    TreeNode c3_r(2);

    root.left = &c_l;
    root.right = &c_r;
    c_l.left = &c2_l;
    c_l.right = &c2_r;
    c2_r.left = &c3_l;
    c2_r.right = &c3_r;

    vector<vector<int>> vec=binaryTreePathSum(&root,5);
    for_each(vec.begin(), vec.end(), [](vector<int> &vecTemp)
    {
        for_each(vecTemp.begin(), vecTemp.end(), [](int value)
        {
            cout << value;
        });
        cout  << endl;
    });
    return 0;
}

运行示例:
《如何理解递归?》

PS:递归算法要积累。慢慢就融汇贯通了,会有一种醍醐灌顶的感觉。

更多文章:http://blog.csdn.net/what951006?viewmode=list
powered by:小乌龟在大乌龟背上~

    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/what951006/article/details/77988580
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞