JZ递归

参考代码:https://blog.csdn.net/u012477435/article/details/83351659?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param

https://blog.csdn.net/m0_37950361/article/details/80668712

JZ-7 斐波那契数列

题目描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。

n<=39

方法一:循环(会超时)

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

方法二:动态规划

选择的最优解为:动态规划, 就避免了递归带来的重复问题。

动态规划解析:

    状态定义:设dp为一维数组,dp[ i ]代表斐波那契的第i个数字
    转移方程:dp[ i + 2] = dp[ i + 1] + dp[ i ],
    初始状态:dp[0] = 0 ,dp[1] = 1
    返回值:dp[i]

空间复杂度的优化:由于本题,在状态转移的过程中,只需要使用到dp[ i + 2] = dp[ i + 1] + dp[ i ],三个数,因此无需使用数组进行存储,可以将空间复杂度从O(n) 降低到 O(1);

可能出现的数字越界问题:
在这里插入图片描述

class Solution {
    public int fib(int n) {
        int a = 0;
        int b = 1;
        int sum = 0;
        if(n == 0){  //处理特殊情况
            return a;
        }
        if( n == 1){
            return b;
        }
        for(int i = 0;i < n - 1; i++){
            sum = (a + b) % 1000000007 ;  //动态方程
            a = b;
            b = sum;
        }
        return sum;
    }
}

方法三:进一步优化

在这里插入图片描述
我们可以将递推式的求解从自顶向下改为自底向上(循环实现)。简而言之,我们已知前两项的值,然后我们就可以用前两项的值求出第3项的值,接着求第4、第5、…,直到求出第n项的值。
实现过程如下图所示,两个相同颜色的箭头可以确定一个新的数列项。
在这里插入图片描述
下面算法的时间复杂度为 O ( n ) O(n) O(n),在面试中够用了,如果还是觉得简单可以继续往下看。

class Solution {
public:
    int Fibonacci(int n) {
        int a = 0,b = 1;
        for(int i = 1;i <= n;i++){
            a = a + b;
            b = a - b;
        }
        return a;
    }
};

方法三:矩阵幂乘(但是自己没有写出来代码)!

在这里插入图片描述

JZ-8 跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

编程思路1(递归)

1级台阶:跳1级 1种跳法
2级台阶:跳1级;跳2级 2种跳法
3级台阶:1级+1级+1级;1级+2级;2级+1级 3种跳法
4级台阶:1级+1级+1级+1级;2级+2级;1级+2级+1级;2级+1级+1级;1级+1级+2级 5种跳法
5级台阶:1级+1级+1级+1级+1级;1级+1级+1级+2级;1级+1级+2级+1级;1级+2级+1级+1级;2级+1级+1级+1级;1级+2级+2 级;2级+1级+2级;2级+2级+1级 8种跳法

总结可发现以下规律:
当台阶数为1级时,有1种跳法;
当台阶数为2级时,有2种跳法;
当台阶数为3级时,有1+2种跳法;
当台阶数为4级时,有2+3种跳法;
当台阶数为5级时,有3+5种跳法;

当台阶数为n级时,有f(n-1)+f(n-2)种跳法。
故可用递归方法解决这一问题。

class Solution {
public:
    int jumpFloor(int number) {
        if(0 == number) return 0;
        else if(1 == number) return 1;
        else if(2 == number) return 2;
        else{
            return jumpFloor(number-1)+jumpFloor(number-2);
        }
    }
};

编程思路2(动态规划)

class Solution {
public:
    int jumpFloor(int number) {
        int a = 1;
        int b = 1;
        int sum = 1;
        if(0 >= number){  //处理特殊情况
            return 0;
        }
        for(int i = 1;i < number; i++){
            sum = (a + b) % 1000000007;  //动态方程
            a = b;
            b = sum;
        }
        return sum;
    }
};

编程思路3(进一步优化)

class Solution {
public:
    int jumpFloor(int number) {
        int a = 1;
        int b = 1;
        if(0 >= number)  return 0;
        for(int i = 1;i <= number; i++){
            a = a + b;
            b = a -b;
        }
        return b;
    }
};

JZ-10 矩阵覆盖

题目描述

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2n的大矩形,总共有多少种方法?
比如n=3时,2
3的矩形块有3种覆盖方法:

思路1:被pass掉的递归法

class Solution {
public:
    int rectCover(int number) {
        if( 0 >= number) return 0;
        else if(1 == number) return 1;
        else if(2 == number) return 2;
        else
            return rectCover(number-1)+rectCover(number-1);

    }
};

思路2:动态规划得人心

class Solution {
public:
    int rectCover(int number) {
        int a = 1,b = 1;
        int sum = 1;
        if(0 >= number) return 0;
        if(1 == number) return 1;
        
        for(int i=1;i < number;i++){
            sum = (a + b)% 1000000007;  //动态方程;
            a = b;
            b = sum;
        }
        return sum;

    }
};

思路3:继续优化

class Solution {
public:
    int rectCover(int number) {
        if(0 >= number)  return 0;
        int a = 1,b = 1;
        for(int i=1;i <=number;i++){
            a = a+b;
            b = a-b;
        }
        return b;
    }
};

JZ-65 矩阵中的路径

题目描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
在这里插入图片描述
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

思路1 :一维数组表示矩阵

class Solution {
public:

    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        //检查输入的合法性
        if(matrix==nullptr || rows<1 || cols<1 || str==nullptr)
            return false;
        //定义一个布尔矩阵,其大小和matrix一样,用来存放元素有没有访问过的标识。
        int length = rows*cols;//矩阵的长度
        bool *Visited = new bool[length];
        int d[4] = {-cols,cols,-1,1};//上下左右
        //初始化矩阵Visited所有元素即为0,即为false
        memset(Visited,0,length);

        for(int i = 0;i < length;i++){
            if(dfs(matrix,Visited,d,length,cols,i,str,0)){ //判断矩阵上的每一个元素是否匹配成功
                delete[] Visited; //删除辅助数组
                return true; //返回匹配成功
            }
        }
        delete[] Visited;        //释放辅助空间
        return false;        //运行至此,说明匹配失败
    }
private:
    bool dfs(char* matrix,bool *visited,int *d,int length,int cols,int index,char* str,int k){
        if(str[k] == '\0')       //如果已经匹配完整个字符序列,到结尾了
            return true; //直接返回true,表示已经匹配完整个字符串
        if(matrix[index] == str[k]){
            visited[index] = true;
            for(int i = 0;i<4;i++){
                if(index+d[i] >= 0 && index+d[i] <= length && visited[index+d[i]]!=true){
                    if (dfs(matrix,visited,d,length,cols,index+d[i],str,k+1))
                        return true;
                }
            }
            visited[index] = false;
        }
        return false;
    }
};

思路2 :二维数组表示矩阵

class Solution {
public:

    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        vector<char> visited(rows*cols,0);
        for(int i=0;i<rows;i++)
            for(int j=0;j<cols;j++){
                if(dfs(matrix,visited,str,i,j,rows,cols)){ //判断矩阵上的每一个元素是否匹配成功
                //delete[] Visited; //删除辅助数组
                return true; //返回匹配成功
                }
            }
        //delete[] Visited;        //释放辅助空间
        return false;        //运行至此,说明匹配失败
    }
private:
    bool dfs(char* matrix,vector<char> visited,char* str,int x,int y,int rows,int cols){
        if(x<0 || x>=rows || y<0 || y>=cols)  //越界的点
            return false;
        if(matrix[x*cols+y]==*str && visited[x*cols+y]==0){
            visited[x*cols+y]=1;
            if(*(str+1)==0) //字符串结尾了,这是最后一个满足的字符
                return true;
            if(dfs(matrix,visited,(str+1),x,y-1,rows,cols) ||
              dfs(matrix,visited,(str+1),x,y+1,rows,cols) ||
              dfs(matrix,visited,(str+1),x+1,y,rows,cols) ||
              dfs(matrix,visited,(str+1),x-1,y,rows,cols))
                return true;
            visited[x*cols+y] = 0;
        }
        return false;
    }
};

JZ-66 机器人的运动范围

题目描述

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

方法1:DFS遍历

根据题目描述,我们可以模拟题目,我们假设一个5x5矩阵,阈值sho=3,如果我们用DFS的话,就相当于“不撞南墙不回头”,我在下面画了一个图,
在这里插入图片描述

最开始,我们在(0,0)的位置,我们假设按照{右,下,左,上}的方向去试探。所以我们走的顺序应该是按照图中的下标走的。
当走到4的时候,发现不能往继续往右边走,并且4个方向都走不通了,那就回溯到3,发现可以走到5,接着就站在5的视角,发现可以走6,就一直按照这个想法。

本题的递归函数就是:首先站在(0,0)的视角,先往右试探,发现可以走,就以下一个为视角,继续做相同的事情。
递归函数模板为:

时间复杂度:O(mn), m,n为矩阵大小,每个元素最多访问过一次
空间复杂度:O(mn)

dfs(){

    // 第一步,检查下标

    // 第二步:检查是否被访问过,或者是否满足当前匹配条件

    // 第三步:检查是否满足返回结果条件

    // 第四步:都没有返回,说明应该进行下一步递归
    // 标记
    dfs(下一次)
    // 回溯
}  
int main() {
    dfs(0, 0);
}
class Solution {
public:
    using V = vector<int>;
    using VV = vector<V>;
    int dir[5] = {-1,0,1,0,-1};
    
    int check(int n){  //检测坐标的加和并返回
        int sum = 0;
        while(n){
            sum += (n%10);
            n /= 10;
        }
        return sum;
    }
    
    void dfs(int x, int y, int shot, int r, int c, int &ret, VV &mark) {
        //检查下标 和 是否访问
        if(x < 0 || x >= r || y < 0 || y >= c || mark[x][y] == 1)
            return ;
        //检查当前坐标是否满足条件
        if(check(x) + check(y) > shot)
            return ;
        //代码走到这里,说明当前坐标符合条件
        mark[x][y] = 1;
        ret += 1;
        
        for(int i = 0;i < 4; ++i)
            //(-1,0),(0,1),(1,0),(0,-1) ===> 上,右,下,左
            dfs(x+dir[i],y+dir[i+1],shot,r,c,ret,mark);
    }
    int movingCount(int threshold, int rows, int cols)
    {
        if(threshold <= 0)
            return 0;
        VV mark(rows,V(cols,-1));
        int ret = 0;
        dfs(0,0,threshold,rows,cols,ret,mark);
        return ret;
    }
};

方法2:BFS遍历

当前图的遍历算法还有bBFS,所以也可以用BFS做。方法一实例的图,用BFS就是如下这样:
图片说明

class Solution {
public:
    using pii = pair<int,int>;
    int dir[5] = {-1,0,1,0,-1};
    int check(int n){  //检测坐标的加和并返回
        int sum = 0;
        while(n){
            sum += (n%10);
            n /= 10;
        }
        return sum;
    }
    
    int movingCount(int threshold, int rows, int cols)
    {
        if(threshold <= 0) return 0;
        int ret = 0;
        int mark[rows][cols];
        memset(mark,-1,sizeof(mark));
        
        queue<pii> q;
        q.push({0,0});
        mark[0][0] = 1;
        
        while(!q.empty()){
            auto node = q.front(); //获取队列的头
            q.pop(); //队列的头出队
            //每次保证进队列的都是满足条件的坐标
            //代码走到这里,说明当前坐标符合条件
            ++ret;
            
            for(int i = 0;i < 4;++i){
                int x = node.first + dir[i];
                int y = node.second + dir[i+1];
                
                //检查下标 和 是否访问
                if(x >= 0 && x < rows && y >= 0 && y < cols && mark[x][y] == -1){
                    if(check(x) + check(y) <= threshold){ //检查当前坐标是否满足条件
                        q.push({x,y});
                        mark[x][y] = 1;
                    }
                }
            }
        }
        return ret;
    }
};

JZ-27 字符串排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

方法一:回溯法|递归

回溯的本质:一种求解效益更高的穷举法。

基本思路:

1 针对所给问题,定义问题的解空间;
2 确定易于搜索的解空间结构;
3 从开始节点出发,以深度优先的方式搜索整个解空间,并在搜索过程中用剪枝函数避免无效搜索。
4 如果当前扩展结点不能再向纵深方向移动,当前节点为死节点。此时,应该往回移动至最近的一个活节点处,并使这个活节点成为当前节点的扩展结点。

思考过程

在这里插入图片描述

回溯过程

在这里插入图片描述

解决方案

class Solution {
public:
    vector<string> res;
    vector<string> Permutation(string str) {
        //回溯。注:回溯是一种算法思想,可以用递归实现
        if(str.size() == 0)  return res;
        permute(str,0);
        sort(res.begin(),res.end());//按字典序输出
        return res;
        
    }
    
    void permute(string s,int begin){ //begin指向当前执行排列操作的字符串的第一个字符
        if(begin == s.size()) res.push_back(s);
        else{
            for(int i = begin;i < s.size();i++){
                if(i != begin && s[i] == s[begin]) continue;  //去重
                swap(s[begin],s[i]);
                permute(s,begin+1);
                swap(s[begin],s[i]);
            }
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值