剑指---算法---动态规划篇(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) {
    
    }
};
/方法一(可以求解问题,但是时间复杂度不满足)
//定义一个数a,用于存放出现过的连续子数组的和,  后面的连续子数组的最大和与a进行比较,大则更新a,否则,不更新
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
      int res = INT_MIN;
        int numsSize = int(nums.size());
        for (int i = 0; i < numsSize; i++)   //注意这里是从0开始,因为1个数也可能是最大和
        {
            int sum = 0;
            for (int j = i; j < numsSize; j++)
            {
                sum += nums[j];
                if(sum > res){    //这里没有用  res = max(res,sum)  个人感觉会更快
                    res = sum;
                }
            }
        }
        return res;
    }
};
/方法二  结合手写稿查看
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;
  }
};

在这里插入图片描述

//方法三
class Solution
{
public:
    int maxSubArray(vector<int> &nums)
    {
        //类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
        int result = INT_MIN;
        int numsSize = nums.size();
        //因为只需要知道dp的前一项,我们用int代替一维数组
        int dp = nums[0];
        result = dp;
        for (int i = 1; i < numsSize; i++)
        {
            dp = max(dp + nums[i], nums[i]);  //对dp进行更新,dp表示的是当前的连续子数组
            result = max(result, dp);
        }

        return result;
    }
};

第三题: 青蛙跳台阶

在这里插入图片描述
这道题的通过率为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++)
导航链接:必刷算法题—排序篇(C++)
导航链接:剑指—树篇(C++)
导航链接:剑指—链表篇(C++)
导航链接:剑指—队列&栈篇(C++)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫恋蝶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值