剑指---算法---动态规划篇(C++)

动态规划篇

第一题: 斐波那契数列

在这里插入图片描述
解题思路:
这道题算是动态规划的入门题了,比较简单,所以放在第一题进行展示,这里还是稍微说一下解题思路。

  • 当n>2时,我们只需要利用通项公式:fib(n)=fib(n-1)+fib(n-2) 即可求出fib(n),由于最后都会回到fib(1)、fib(0)的位置,所以需要我们求出fib(1)、fib(0)
  • 由已知条件第一个位置和第二个位置的数为1,即fib(2)=fib(1)=1,而fib(0)=fib(2)-fib(1)=0,因此当n=1或n=0时,fib(n)=0

代码部分:

/答题模板
class Solution {
    
    
public:
    int Fibonacci(int n) {
    
    

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

第二题: 连续子数组的最大和

在这里插入图片描述


解题思路:
方法一:
以输入array=[1,-2,3,10,-4,7,2,-5]为例

  • 我们先从第一个数array[0]开始,可以求出array[0],array[0]+array[1],…,array[0]+…+array[7]的八组数之和,然后找到这8组数中最大的数,记最大的数为res2,
  • 然后我们对第二个数array[1]继续以上述的方法,如果第二个数中的组数之和大于res2,那么更新res2
  • 直到数组结束为止,图片形式的过程如下:
    在这里插入图片描述
    方法一的理解比较容易,但是写出的代码时间复杂度超过了,只跑过了11个用例。话说最后一个用例中的数组是真的大,成千上万个数了。在这里插入图片描述
    方法二: 如下图所示:
    在这里插入图片描述

代码部分:

/答题模板
class Solution {
    
    
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
    
    
    
    }
};
/方法一(可以求解问题,但是时间复杂度不满足)
class Solution {
    
    
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
    
    
    int res,res2,temp;
    for(int i=0;i<array.size();i++){
    
      //从第一个数开始
        res=temp=array[i];
        for(int j=i+1;j<array.size();j++){
    
       //从
            temp=temp+array[j];
            if (temp>res){
    
    
                res=temp;
            }
        }
        if (i==0||res2<res){
    
    
           res2=res; 
        }
    }    
        return res2;
    }
};
/方法二
class Solution {
    
    
public:
  int FindGreatestSumOfSubArray(vector<int> array) {
    
    
      int len=array.size();
      // 特判为空的情况
      if(len==0){
    
    
          return 0;
      }
      vector<int> pre;
      int sum=0,ans;
      // 求前缀和
      for(int i=0;i<len;i++){
    
    
          sum=sum+array[i];
          pre.push_back(sum);
      }
      ans=max(pre[0],pre[len-1]);  //ans为S[0]和S[len-1]的最大值
      
      // 枚举连续数组的长度,中间数组的最大值
      for(int i=0;i<len;i++){
    
    
          for(int j=i+1;j<len;j++){
    
    
              ans=max(ans,pre[j]-pre[i]);
          }
      }
      return ans;
  }
};

第三题: 青蛙跳台阶

在这里插入图片描述
这道题的通过率为39%,奈何我不会做,扎心了。故这里借鉴牛客网漫天云天自翱翔的解题思路,加上我自己在补充一点解释,来攻克这道题。

解题思路:

  • 假设有n阶台阶,在所有的跳法中,由于青蛙一次只能跳1步或者2步,所以青蛙跳上最后一阶只能由f(n-1)+1 或者f(n-2)+2 这两种情况得来。
    即:f(n)=f(n-1)+f(n-2)
  • 同理:f(n-1)=f(n-2)+f(n-3),以此类推,我们可以得到下面这幅图。在这里插入图片描述
  • 相应代码在代码部分给出了,可以通过提交。但是在这之前,我们再仔细分析一波,如下图所示:在这里插入图片描述
  • 从图中我们可以看到,以这种方式虽然最后可以算出最终结果,但是在求解过程中,有些结果会被重复计算,这无疑造成了一部分不必要的开销。
  • 解决方法: 可以采用一个map存储已经被计算过的值(不采用vector的原因,不确定台阶的数量,无法预先申请确定大小的数组)。

最后,我们给出了两种解决的代码,可以发现,优化后的代码效果更好!


代码部分:

/答题模板
class Solution {
    
    
public:
    int jumpFloor(int number) {
    
    
        
    }
};
/未优化前  运行时间:147ms  占用内存:564KB
class Solution {
    
    
public:
    int jumpFloor(int number) {
    
    
        if(number<=1) return 1;
        return  jumpFloor(number-1)+jumpFloor(number-2);
    }
};

//以台阶=4为例,最后的返回结果是
return jumpFloor(3)+jumpFloor(2);
->return jumpFloor(2)+jumpFloor(1)+jumpFloor(1)+jumpFloor(0);
->return 3*jumpFloor(1)+2*jumpFloor(0);
->return 5;
/优化后  运行时间:3ms   占用内存:548KB
class Solution {
    
    
public:
    map<int,int> map_flag;//记录已经被计算出来的值
    int jumpFloor(int number) {
    
    
        if(number<=1) return 1;//初始两个台阶的结果
        if(map_flag.count(number)){
    
    //map.count(Key)的意思是,如果map中存在key,则返回值为1否则为0。
            return map_flag[number];
        }
        return  map_flag[number]=jumpFloor(number-1)+jumpFloor(number-2);//未被计算过,存入map;
    }
};

第四题: 跳台阶拓展问题

在这里插入图片描述
解题思路:

这道题说白了就是找通项公式的问题:

  • 以台阶数n=5为例,我们可以发现,青蛙跳台阶存在着这样一个通项公式:
    当n<2时, f ( n ) = 1 {f(n) = 1} f(n)=1 ;当n≥2时, f ( n ) = 2 n − 1 {f(n) = {2^{n - 1}}} f(n)=2n1(方法一)

  • 其实也可以理解为当n≥2时, f ( n ) = 2 ∗ f ( n − 1 ) {f(n) = 2*f(n - 1)} f(n)=2f(n1)(方法二)

  • 分析过程如下图所示:在这里插入图片描述


代码部分:

/代码模板
class Solution {
    
    
public:
    int jumpFloorII(int number) {
    
    

    }
};
/方法一
class Solution {
    
    
public:
    int jumpFloorII(int number) {
    
    
    int res=0;
    if(number<=1) return 1;//初始两个台阶的结果
    return  res=pow(2,number-1);  //pow(a,b)表示求a的b次方
    }
//不用pow,换成return  res=1<<(number-1)也可以,左移表示乘2
};
/方法二(类似于第三题)
class Solution {
    
    
public:
    map<int,int> map_flag;//记录已经被计算出来的值
    int jumpFloorII(int number) {
    
    
        if(number<=1) return 1;//初始两个台阶的结果
        if(map_flag.count(number)){
    
    //map.count(Key)的意思是,如果map中存在key,则返回值为1否则为0。
            return map_flag[number];
        }
        return  map_flag[number]=2*jumpFloorII(number-1);//未被计算过,存入map;
    }
};

第五题: 买卖股票的最好时机

在这里插入图片描述
解题思路:
方法一:暴力法

  • 以输入【2,4,1】为例,设i为买入的时机,j为卖出的时机 (i<j)
  • 从i=0开始,计算后面卖出的利润,会得到profit={2,-1}两个值,其中2=4-2;-1=1-2。
  • 同理,当i=1时,profit={-3}。
  • 找到上面两者的最大值2,即为最大利润。在数组长度变长时,该方法依旧使用。
  • 缺点,时间复杂度为O(N²)

方法二:贪心算法
在这里插入图片描述

  • 为了达到利润最大化,必然会挑一个历史最低点进行买入
  • 声明最低价格minVal
  • 那么在第i天卖出的股票的利润就是Prices[i]-minVal

代码部分:

/答题模板
class Solution {
    
    
public:
    /**
     * 
     * @param prices int整型vector 
     * @return int整型
     */
    int maxProfit(vector<int>& prices) {
    
    
        // write code here
    }
};
/方法一
class Solution {
    
    
public:
    int maxProfit(vector<int>& prices) {
    
    
        int max_profit = 0;//max用于保存最大利润
        int i,j;
        for(i = 0;i<prices.size();i++){
    
    
            for(j = i+1;j<prices.size();j++){
    
    
                if(prices[j] - prices[i] > max_profit ){
    
    
                    max_profit = prices[j] - prices[i];//如果当前利润值大于历史最佳利润值,则更新
                }
            }
        }
        return max_profit ;//返回最大值
    }
};
/方法二
class Solution {
    
    
public:
    int maxProfit(vector<int>& prices) {
    
    
        int minVal = prices[0], max_profit = 0;   //定义minVal用来存最小的价格,max_profit为我们最终找到的最大利润
        for (int i = 1; i < prices.size(); ++i) {
    
    
            if (prices[i] < minVal) minVal = prices[i];
            max_profit = max(max_profit , prices[i] - minVal);
        }
        return max_profit;
    }
};

注: 以上题目都是在牛客网的剑指offer题库中刷的,有自己做的,也有不会做看别人精华解题思路,然后总结的。如果侵犯了您的权益,请私聊我。
最后,觉得本文内容对你有所帮助的话,感谢点赞收藏!

算法篇的题库还在持续更新中,小伙伴们不要着急。
导航链接:剑指—链表篇(C++)
导航链接:剑指—队列&栈篇(C++)

猜你喜欢

转载自blog.csdn.net/qq_40077565/article/details/121364009
今日推荐