📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:练题
🎯长路漫漫浩浩,万事皆有期待
文章目录
- 买卖股票的最佳时机 II
- 跳跃游戏
- 跳跃游戏 II
- 总结:
买卖股票的最佳时机 II
122. 买卖股票的最佳时机 II - 力扣(LeetCode)
乍一看题目要求十分困难,求出总利润最高,要求是某一天买某一天卖,只能同时持有一支股票,但是可以在卖出后再其他时间再次购买彩票并卖出,这一点是十分重要的,这也间接的加大了难度。
解题思路是:我们可以将任意天数购买和卖出的等式简化为以下形式,例如我们购买该彩票的时间为第0天而在第3天时卖出,那么对于prices为数组名,有prices[3]-prices[0]=(prices[3]-prices[2])+(prices[2]-prices[1])+(prices[1]-prices[0])。这一等式可以将其看作为通式,它适用于任何天数的购买和卖出,都有售出价格减去购买价格等于其从卖出下标到购买下标方向上的左闭右闭区间相邻数的差再相加。这一结论可以用任意数组来模拟,都是正确的,起初看到这一方法觉得十分的神奇,后来想了一想由于是左闭右闭区间,相邻数做差再相加,那么说明我们的购买钱数和卖出钱数实际都参与了运算,此时我们将它们两个中间的数据作为一个整体,设购买钱数为x,卖出为y,那么很容易知道y-x=y-z+(z-x)。所以等式一定是成立的。我们根据这一特点,可以想到如果有一数组来表示给定数组prices中相邻两个数的差,那么将正数相加获得的答案自然就是买卖股票的最大利润。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result=0;
for(int i=1;i<prices.size();i++)
{
result+=max(prices[i]-prices[i-1],0);
}
return result;
}
};
知道了解题思路,不难写出以上代码,这里我们用result的重复判断来避免了新开数组的空间开辟,从而使代码变得简洁一些,实际上这和开辟一个数组存储相邻差,再取正数做和思路是相同的。
跳跃游戏
55. 跳跃游戏 - 力扣(LeetCode)
也是道没做过很难做的题目,思路是不要纠结于本次的前进究竟要走到哪一个位置,而是明确走的这一步它的最大范围能够覆盖到哪?如果覆盖范围能够包括最后一个位置,那么就返回true。而每一次的for以当前能够走到的最远距离作为判断的范围。
class Solution {
public:
bool canJump(vector<int>& nums) {
int cover=0;
if(nums.size()==1)
{
return true;
}
for(int i=0;i<=cover;i++)
{
cover=max(i+nums[i],cover);
if(cover>=nums.size()-1)
{
return true;
}
}
return false;
}
};
cover每次对当前数据进行比较,如果比现有范围大就赋值给它,判断部分的i<=cover在这里起的是什么作用呢?i每次都++如果cover所覆盖的范围都是0,那么就直接跳出循环了,每次它是用来约束i的,用来判断到底有没有可行的范围可以走,那有人问,如果是{2,0,1,0}怎么办呢?这就凸显出nums[i]+i的作用了,从当前下标开始最多能走到哪,所以加i是十分重要的,不能够缺少。
至于为什么cover>=nums.size()-1呢,是因为最后一个数下标是这个,所以我们判断覆盖范围大于或等于直接返回true了。
实际上这种方法是十分巧妙的,可以随意模拟数组将代码带入进行解析,这种cover赋值更大范围的思想,有效的帮助我们解决如果本次可以走的范围很多,那么到底走向哪里的问题,不用像递归思路那样走错了需要回溯走下一个位置,因为即使走错了,下一步的cover范围覆盖可以帮助我们修改,十分的巧妙。例如数组{2,0,1,2,1,3,0,1,0}比如说我们走到第二个2时候,如果是回溯思想可能就会先走第二个1试一试,不行再走3试一试,不过这里的cover会直接覆盖掉上个答案,也就是说他尽可能取得最大的步骤。
跳跃游戏 II
45. 跳跃游戏 II - 力扣(LeetCode)
跳跃游戏II和跳跃游戏的前部分思路是一致的,都是要确立覆盖的范围,而并非要一次确定好走哪一个下标处,不同的是,这道题目不仅要求要走到最后一个位置,还要求要使得走的步数最小。
这该怎么办呢?我们需要创建两个变量来存储能覆盖的范围,一个是当前的另一个是下一步能覆盖到的范围,那么我们如何知道当前这一步的范围内,下一步最远能走多远呢?答案是我们用循环变量i来控制,如果不停的更新next的最大值,当i等于当前的最大范围时,进入判断,把next值直接赋值给当前的最大范围,然后判断此范围是否包含最后一个数的下标,不包含接着循环这些步骤,直到到达。
class Solution {
public:
int jump(vector<int>& nums) {
if (nums.size() == 1)
{
return 0;
}
int curDistance = 0; // 当前覆盖最远距离下标
int ans = 0; // 记录走的最大步数
int nextDistance = 0; // 下一步覆盖最远距离下标
for (int i = 0; i < nums.size(); i++)
{
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标
if (i == curDistance)
{ // 遇到当前覆盖最远距离下标
ans++; // 需要走下一步
curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了)
if (nextDistance >= nums.size() - 1)
{
break; // 当前覆盖最远距到达集合终点,不用做ans++操作了,直接结束
}
}
}
return ans;
}
};
我们把判断跳出的部分写在了第一个if判断的后面,这样可以避免一些时间的浪费,提高效率,不过理论上来上说,放进去也是可以的
总结:
今天我们完成了买卖股票的最佳时机 II、跳跃游戏、跳跃游戏 II三道题,相关的思想需要多复习回顾。接下来,我们继续进行算法练习。希望我的文章和讲解能对大家的学习提供一些帮助。
当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~