经典算法之递归(Recursion)

1、递归的定义

  递归:你打开面前这扇门,看到屋里面还有一扇门(这门可能跟前面打开的门一样大小(静),也可能门小了些(动)),你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开,。。。, 若干次之后,你打开面前一扇门,发现只有一间屋子,没有门了。 你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这钥匙开了几扇门。

  循环:你打开面前这扇门,看到屋里面还有一扇门,(这门可能跟前面打开的门一样大小(静),也可能门小了些(动)),你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,(前面门如果一样,这门也是一样,第二扇门如果相比第一扇门变小了,这扇门也比第二扇门变小了(动静如一,要么没有变化,要么同样的变化)),你继续打开这扇门,。。。,一直这样走下去。 入口处的人始终等不到你回去告诉他答案。

2、递归的思想

  递归的基本思想是把规模大的问题转化为规模小的相似的子问题来解决。在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。另外这个解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。

这里写图片描述

3、递归的要素

(1)明确递归终止条件

  我们知道,递归就是有去有回,既然这样,那么必然应该有一个明确的临界点,程序一旦到达了这个临界点,就不用继续往下递去而是开始实实在在的归来。换句话说,该临界点就是一种简单情境,可以防止无限递归。

(2)给出递归终止时的处理办法

  我们刚刚说到,在递归的临界点存在一种简单情境,在这种简单情境下,我们应该直接给出问题的解决方案。一般地,在这种情境下,问题的解决方案是直观的、容易的。

(3)提取重复的逻辑,缩小问题规模

  我们在阐述递归思想内涵时谈到,递归问题必须可以分解为若干个规模较小、与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。从程序实现的角度而言,我们需要抽象出一个干净利落的重复的逻辑,以便使用相同的方式解决子问题。

function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题
        solve;                // 递去
        recursion(小规模);     // 递到最深处后,不断地归来
    }
}
function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题
        recursion(小规模);     // 递去
        solve;                // 归来
    }
}

4、递归的经典应用

  一道二叉树的题型,打印出根到叶等于5的所有节点:

#include <bits/stdc++.h>
using namespace std;

vector<vector<int>> g_vec;
const int target = 5;

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)//如果减到了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);
}

int main(int argc, char const *argv[])
{
    TreeNode root(1);
    TreeNode c1_l(2);
    TreeNode c1_r(4);
    TreeNode c2_l(2);
    TreeNode c2_r(1);
    TreeNode c3_l(1);
    TreeNode c3_r(2);

    root.left = &c1_l;
    root.right = &c1_r;
    c1_l.left = &c2_l;
    c1_l.right = &c2_r;
    c2_r.left = &c3_l;
    c2_r.right = &c3_r;

    vector<int> vec;
    ForEach(&root, target, vec);
    for (auto g : g_vec)
    {
        for (auto v : g)
            cout << v << " ";
        cout << endl;
    }
    return 0;
}

参考:https://blog.csdn.net/sinat_38052999/article/details/73303111
https://blog.csdn.net/theknotyouknow/article/details/24435291
https://blog.csdn.net/what951006/article/details/77988580

猜你喜欢

转载自blog.csdn.net/daaikuaichuan/article/details/80724642