算法学习Day32买股票、跳跃游戏
Day32买股票、跳跃游戏
By HQWQF 2024/01/18
笔记
122.买卖股票的最佳时机 II
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
总利润为 4 + 3 = 7 。
示例 3:
输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。
贪心算法
贪心算法的本质是短视,只选择在当前看来最好的选择。
带入日常的经验,我们可能会觉得,在股票价格为1时买入,第二天发现涨到了5,这时我们会想如果价格涨到了6再卖出岂不是赚的更多。但是其实如果我们在价格为6时卖出价格为1的股票,此时获利5,但是如果我们在价格为5时就卖出然后再买入价格为5的股票,再在价格为6时卖出,此时获利为:5-1 + 6-5 = 5,实际上是一样的获利,其实我们对于这样的情况,只需要在每天看看明天股价,如果涨就卖今天的股票然后明天卖出。
所以实际上,知道一段未来时间内的股价情况和只知道明天的股价情况带来的获利理论上都是一样。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result = 0;
for(int i = 0;i < prices.size() - 1;i++)
{
//如果明天价格比今天高,就买入当日股票并明天卖出
if(prices[i] < prices[i+1])
{
result += prices[i+1] - prices[i];
}
}
return result;
}
};
55. 跳跃游戏
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
示例 1:
输入: nums = [2,3,1,1,4]
输出: true
解释: 可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入: nums = [3,2,1,0,4]
输出: false
解释: 无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
贪心算法
我们可以发现如果可以使用i = i + nums[i]发生在数组中跳跃,在某次跳跃后发现i + nums[i] >= nums.size()-1就说明可以到达最后一个下标。
但是如果最终进入死循环,也不意味着不行,此时我们需要一些回溯。但是还有更加简单的方法。
我们可以从最左边开始维护一个cover变量,代表我们当前可以到达的最远的下标,遍历i到cover中的所有下标,并且每次遍历都更新cover的值,如果cover>= nums.size()-1就说明可以到达最后一个下标。
也就是确定当前能到达的最大范围,然后迈一小步,继续确定当前能到达的最大范围,再迈一小步,如果能到达的最大范围够大就可以。
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
cover = max(i + nums[i], cover);
if (cover >= nums.size() - 1) return true; // 说明可以覆盖到终点了
}
return false;
}
};
45.跳跃游戏 II
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
贪心算法
为了得出最小的步数,想前面那样一步一步蹭的方法就不行了,我们可以遍历整个数组,先确定当前可以达到的最大的下标a,然后遍历到这个下标a,遍历过程中维护一个新的可以到达的最大下标b,遍历到下标a后更新下标a为下标b,此时相当于跳了一跳。
遍历到下标a并维护下标b相当于确定当前能到达的所有跳跃目的地上下一步能到达的范围。关键是有一个明确的时机相当于跳了一跳。
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;
}
};
实际上,由于题目的数据集确保我们一定可以到达终点,break的语句就没有必要了,但是直接去掉也不行,因为在i == nums.size()-1(倒数第一个元素)时,如果此时正好i == curDistance,就会增加跳跃次数,然而此时已经到达了末尾,所以会导致此时结果多了1,所以我们遍历到nums.size()-2(倒数第二个元素)就行了。
class Solution {
public:
int jump(vector<int>& nums) {
int curDistance = 0; // 当前覆盖的最远距离下标
int ans = 0; // 记录走的最大步数
int nextDistance = 0; // 下一步覆盖的最远距离下标
for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1,这是关键所在
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标
if (i == curDistance) { // 遇到当前覆盖的最远距离下标
curDistance = nextDistance; // 更新当前覆盖的最远距离下标
ans++;
}
}
return ans;
}
};

浙公网安备 33010602011771号