递归与深搜入门

弱校讲课,大牛退散

简单递归+DFS入门

这里介绍,递归,深度优先搜索,的简单入门,这两个概念在下面都会提到。

递归:就是函数自身调用自身的过程,举个例子

我的递归思路,代码风格

最前面写结束条件

下面写递归操作

1 递归写阶乘

int f(int n)
{
    if(n == 0) return 1; // 0的阶乘为1
    return n*f(n-1);
}

我们模拟一下这个过程。

时间复杂度分析


2 爬台阶:每步只能走一或两个台阶,求从第1层 爬到 第n层台阶共有多少种走法。

首先我们正着想,f[N]表示走到第n层的方案数

n == 1, f[1] = 1

n == 2, f[2] = 1

n > 2,f[n] = f[n-1] + f[n-2],走到n-2层走两层 + 走到n-1层走一层。

不难发现其实就是斐波那契数列

换种方法,我们再反着想,

f[n] 只可能从第f[n-1] 和 f[n-2] 走来,所以求f[n] 即求 f[n-1],f[n-2]的和

f[n] = f[n-1] + f[n-2] = f[n-2]+f[n-3]+f[n-3]+f[n-4] = …………一直到f[1] 时我们知道解为1,f[2] = 1。

(思考结束条件为什么需要两个,结束条件能不能写其他数量个)

我们可以尝试着实现下这个反向的过程

int f(int n) // 此代码需保证 n 为正整数
{
    if(n == 1 || n == 2) return 1;
    return f(n-1) + f(n-2);
}

我们在模拟一下这个过程。

时间复杂度分析

开个数组优化,这里带了点记忆化搜索的感觉(这个不属于今天讲的范围,但是可以作为提高要求自行提前了解)

#include <stdio.h>

int dp[50]; // 记忆化,记忆化有点DP的感觉所以我选择取名为DP

int f(int n)
{
    if(dp[n]) return dp[n];
    return dp[n] = f(n-1) + f(n-2);
}

int main()
{
    dp[1] = 1;
    dp[2] = 1;
    printf("%d\n",f(20));
    return 0;
}

时间复杂度分析


3 递归写10层for循环嵌套代码如下

for(int i = 1; i <= 5; ++i)
    {
        for(int j = 1; j <= 5; ++j)
        {
            for(int k = 1; k <= 5; ++k)
            {
                ………………
            }
        }
    }

改成递归形式,第一次调用传个10即10层for循环嵌套,可见递归是非常的灵活

void wtf(int step)
{
    if(step == 0) return;
    for(int i = 1; i <= 5; ++i)
    {
        wtf(step-1);
    }
}

递归与栈的关系 括号配对


DFS(深搜):不撞南墙不回头

画个迷宫1表示墙,0表示路,,问左上角到右下角是否存在路径

0, 1, 0, 0, 0

0, 1, 1, 1, 0

0, 0, 0, 0, 0

0, 1, 1, 1, 0

0, 0, 0, 1, 0

我们可以傻傻的按照

if(能走到一个没走到的点)到那个点;

else 回到过来的点;

按下左上右顺序模拟下

#include <stdio.h>
#include <string.h>

// 可以借助全局变量表示状态
int flag = 0; // 0没找到,1找到
int mov[4][2] = {0,1,1,0,0,-1,-1,0}; // 其实就是四个方向,自己可以检验
int mp[10][10] = /*可以自行修改图形实验*/
{
    {0, 1, 0, 0, 0},

	{0, 1, 1, 1, 0},

	{0, 0, 0, 0, 0},

	{0, 1, 1, 1, 0},

	{0, 0, 0, 1, 0},

};
// map缩写,存图
int vis[20][20]; // 避免来回走死循环,而且某些情况可以优化下面会讲

void dfs(int x, int y)
{
    if(x == 4 && y == 4)
    {
        flag = 1;
        return;
    }
    for(int i = 0; i < 4; ++i)
    {
        int xx = x+mov[i][0];
        int yy = y+mov[i][1];
        // 如果越界,或者这个点来过,或者这点是墙 则跳过
        // 先判断越界也很关键,或者的前面已经为真后面不会执行(因为执行不执行都是真),
        // 同理&&前面为假,后一半则不执行(因为执行不执行都是假)
        // 先判越界避免了vis和mp访问越界如-1(会运行时错误)
        if(xx < 0 || yy < 0 || xx > 4 || yy > 4 || vis[xx][yy] || mp[xx][yy]) continue;
        vis[xx][yy] = 1;
        dfs(xx,yy);
        if(flag) return; // 如果找到则返回,这里加不加的区别
//        vis[xx][yy] = 0; // 这里加不加的区别
    }
}

int main()
{
    //多组输入记得初始化vis
    vis[0][0] = 1;
    dfs(0,0);
    printf("%d\n",flag);
    return 0;
}

时间复杂度分析

不开vis,会死循环

vis只在递归标记的情况,(更快的寻找

vis回溯时再取消标记的情况,(寻找多条

不用vis可以怎么修改?


题目

HDU 2041 楼梯

洛谷1028

nyoj 2 数据水了,没有最后判断栈是否为空的数据 (((() 这个数据自己过下

nyoj 32(需要知道字典序,一个小概念)

OpenJ_Bailian - 4147 汉诺塔

HDU 1016 剪枝

nyoj 1058

HDU 1241 这个及下面都是图的深搜

POJ 3984 需要路径输出,带点回溯操作

HDU 1010 回溯带点操作,剪枝优化

有意思的操作

递归求GCD,(代码一行比较精简)

int gcd(int a, int b)
{
    return a%b ? gcd(b, a%b) : b;
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值