Leetcode - 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 。
示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 总利润为 4 。
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。
笔记:
这道题做起来感觉特别绕,考虑的因素很多,要考虑什么时候买入,什么时候卖出,又要考虑买入和卖出的顺序是否合法。
看完题解就清晰很多了,这道题的思路是取当天利润为正利润的天数进行售卖,但到了这里我就有了疑问:为什么每天的利润是从今明两天的价格差而出是跨度好几天呢?
也就是为什么第二天的股票不能等到第四天再卖么,这样不是利润更大么?这里我们可以带入运算一下,第二天买入第三天卖出再第三天买入第四天卖出,得到利润为4 + 5 = 9,而跨度两天:第二天买入,第三天不动因为不能够买入,所持股票数不能超过1,所以第四天卖出,获得利润10 - 1 = 9.这样的利润是一致的,我们可以详细的拆分一下第一种情况:-1 + 5 - 5 + 10,会发现第三天只是一个媒介并不影响结果。所以我们就可以选取每日利润为正的加入到res中。
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<int> profit(prices.size(), 0);
int res = 0;
for(int i = 1; i < prices.size(); i++){
profit[i] = prices[i] - prices[i - 1];
}
for(int i = 1; i < profit.size(); i++){
if(profit[i] > 0){
res += profit[i];
}
}
return res;
}
};
Leetcode - 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 , 所以永远不可能到达最后一个下标。
笔记:
这道题我最开始的思路是:选取当前节点所能够跳跃的区间内跳跃值最大的位置,然后将指针移动到那个位置,在循环的表层我们设置一个判断条件,判断当前节点能否到达最后一个节点。
class Solution {
public:
bool canJump(vector<int>& nums) {
int n = nums.size();
if (n == 0) return false; // 处理空数组的边界情况
if (n == 1) return true; // 处理只有一个元素的边界情况
int currIdx = 0;
while (currIdx < n - 1) {
if (nums[currIdx] == 0) {
return false;
}
int maxJumpIdx = currIdx + 1;
for (int i = currIdx + 2; i <= currIdx + nums[currIdx]; i++) {
if (i < n && nums[i] + i >= nums[maxJumpIdx] + maxJumpIdx) {
maxJumpIdx = i;
}
}
if (maxJumpIdx >= n - 1) {
return true;
}
currIdx = maxJumpIdx;
}
return false;
}
};
但是效率非常之低,可能是写法上的问题:
class Solution {
public:
bool canJump(vector<int>& nums) {
if(nums.empty()) return false;
int cover = 0;
for(int i = 0; i <= cover; i++){
cover = max(cover, i + nums[i]);
if(cover >= nums.size() - 1){
return true;
}
}
return false;
}
};
Leetcode - 45:跳跃游戏:
题目:
给定一个长度为 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
笔记:
这道题我的思路是在每次计算出cover后就在count+1,但是遇到了一个问题就是当遇到的节点值>1时,i到cover需要走好几步,就需要加额外的count导致不符合条件。
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() == 1) return 0;
if(nums.empty()) return 0;
int count = 0;
int cover = 0;
for(int i = 0; i <= cover; i++){
cover = max(cover, i + nums[i]);
count++;
if(cover >= nums.size() - 1){
return count;
}
}
return 0;
}
};
还是背上一个题目给混淆了,以为可以在上一个题目的基础上稍微修改一下即可。但是我们滤清思路后发现并不是这样的:
本题的思路是:从第一个元素出发,记录当前节点位置为0,最大位置为0,然后开始遍历,遍历第一个节点,计算该节点能到达的最大位置,当遍历到当前元素时count++,然后将最大位置赋值给当前位置,也就是说移动了一步,但我们还是一步一步的遍历并不是一次跨越好几步。当我们到达当前位置,我们就重复上面步骤。
下面这则代码是将curdis直接跳到了当前几点的最远位置,但是我们忽略了,在当前节点到最远节点这个区间内会存在一个可以跳跃的更远的节点,这就导致了max的取值并不规范:
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() == 1) return false;
int curdis = 0;
int maxdis = 0;
int count = 0;
while(curdis < nums.size()){
maxdis = max(maxdis, curdis + nums[curdis]);
count++;
curdis = maxdis;
if(maxdis >= nums.size() - 1) break;
}
return count;
}
};