122买卖股票的最佳时机
题目描述
给你一个整数数组 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。
代码1(自己的思路)挺有意思的
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result=0;
for(int i=0;i<prices.size();i++){
int j=0;int temp=0;
for(j=i;j<prices.size()-1;j++){
if(prices[j+1]<prices[j]){
break;
}
}
temp=prices[j]-prices[i];
result+=temp;
i=j;
}
return result;
}
};
/*主要思路就是在for循环里找到连续上升的子串,遇到跌就停止,记录并保存到结果里
每次的第二层for都从 i 开始,而 i 在diercengfor循环找到连续的重点时要更新到终点*/
答案代码:
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;
}
};
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:
// 函数 canJump 接受一个整数数组 nums,返回一个布尔值表示是否可以到达数组的最后一个下标
bool canJump(vector<int>& nums) {
int cover = 0; // cover 变量用于表示当前能够覆盖的最远位置
// 如果数组的大小为 1,说明已经在最后一个下标,直接返回 true
if (nums.size() == 1) return true;
// 遍历每一个可以到达的位置
for (int i = 0; i <= cover; i++) {
// 更新 cover 变量为当前最远可达位置 i + nums[i]
cover = max(cover, i + nums[i]);
// 如果 cover 已经覆盖了或超过了最后一个下标,返回 true
if (cover >= nums.size() - 1) return true;
}
// 如果循环结束仍然无法到达最后一个下标,返回 false
return false;
}
};
在实现跳跃游戏的过程中,有几个常见的易错点需要注意,以下是一些可能的易错点及其解释:
-
数组为空或只有一个元素的处理:
- 如果输入的
nums
数组为空,或者只有一个元素,代码应该能正确处理这些边界情况。在上述代码中,已经通过if (nums.size() == 1) return true;
处理了一个元素的情况,但在这里最好也考虑到数组为空的情况。
- 如果输入的
-
循环条件设置错误:
- 在
for
循环中,条件是i <= cover
。这意味着如果cover
变为负数或达不到任何位置时,可能会导致循环错误或无限循环。要确保cover
的值合理并能覆盖到更新时的下标设置。
- 在
-
越界访问:
- 更新
cover
时可能会越界,例如在i + nums[i]
计算时,如果i
是最后一个合法下标,nums[i]
可能导致越界。要确保cover
不超过数组的边界。
- 更新
-
逻辑判断混乱:
- 使用
max(cover, i + nums[i])
准确反映当前可达的最远位置,但需要保证逻辑判断对每一步的更新是合理的。如果cover
的初始值设得不合理可能导致错误的判断。
- 使用
-
返回条件的处理:
- 在循环内部更新
cover
后,立即判断是否达到目标。如果漏掉这个判断,将导致函数始终返回false
,即使可以到达最后一个元素。
- 在循环内部更新
-
处理非负数的约定:
nums
应为非负整数,但在调用时需要确保这个约定得以遵守,否则可能导致逻辑错误或运行时异常。
45跳跃游戏2
题目描述:
给定一个长度为 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
代码:
class Solution {
public:
int jump(vector<int>& nums) {
// 如果数组的大小为 1,说明已经在目标位置,跳跃次数为 0
if (nums.size() == 1) return 0;
int curDistance = 0; // 当前覆盖的最远距离下标
int ans = 0; // 记录需要的跳跃次数
int nextDistance = 0; // 下一步可以覆盖的最远距离下标
// 遍历数组中的每个元素
for (int i = 0; i < nums.size(); i++) {
// 更新能够到达的最远位置(i + nums[i]),
// 利用 max 函数保持 nextDistance 为更大值
nextDistance = max(nums[i] + i, nextDistance);
// 如果当前索引 i 达到了当前覆盖的最远距离
if (i == curDistance) {
ans++; // 需要进行一次跳跃
curDistance = nextDistance; // 更新当前覆盖的最远距离
// 如果当前覆盖的距离已经可以达到或超过数组的最后一个下标,直接结束
if (nextDistance >= nums.size() - 1) break;
}
}
// 返回所需的跳跃次数
return ans;
}
};
这个代码需要理解想象代入数据
代码解释:
-
初始化:
curDistance
记录当前能达到的最远位置的索引。ans
用于计数跳跃的次数。nextDistance
记录在当前层能够跳到的最远位置。
-
基础检测:
- 如果数组长度为 1,说明已经在目标位置,跳跃次数为 0,直接返回。
-
遍历数组:
- 使用
for
循环遍历整个nums
数组。 - 在每一步中,更新
nextDistance
,计算当前下标i
所能够达到的最远位置nums[i] + i
。
- 使用
-
判断需要跳跃:
- 当遍历到当前
curDistance
的时候,需要做一次跳跃 (ans++
)。 - 更新
curDistance
为nextDistance
,准备进行下一轮跳跃。
- 当遍历到当前
-
终止条件:
- 如果此时的
nextDistance
大于或等于nums.size() - 1
,说明已经能够到达或覆盖最后一个元素,直接退出循环。
- 如果此时的
-
返回结果:
- 最后返回
ans
,即所需的总跳跃次数。
- 最后返回
1. 概念理解
当你在处理某个范围或区间时,可能会遇到多个范围相互重叠的情况。在跳跃游戏的上下文中,可以将每个数字视为从当前索引可以到达的最大索引。
- 例如,如果
nums[i] = 3
,表示从索引i
点出发,你可以跳到i+1
、i+2
或i+3
这几个索引。这意味着该点对后面的多个位置都是可达的。
2. 重叠区间的识别
在每次判断可达性的过程中,我们要识别当前覆盖的最远距离和下一个覆盖的最远距离之间的关系。
- 当前可达的最远距离 (
curDistance
):这是你目前已经知道可以覆盖的最远位置。 - 下一步可达的最远距离 (
nextDistance
):这是在到达当前距离后,通过遍历已知的信息,计算出来的下一步可以到达的最远位置。
当 i
达到 curDistance
时,意味着你已经探索到了当前能够覆盖的所有索引。接下来,你需要更新 curDistance
为 nextDistance
,以为下一步的跳跃做好准备。
3. 重叠部分的操作
当你更新 nextDistance
时,你实际上是在不断扩展可到达的范围。如果有多个点 Green,Red,Blue 的区域重叠, 你需要:
-
维护最远到达的位置:如在更新
nextDistance
时,要比较当前已知最远位置和遍历到的所有可达位置之间的重叠部分。 -
判断覆盖会造成的影响:在每次达到
curDistance
时,查看nextDistance
是否大于当前目标位置,若是, 则可以早期退出。
示例分析
考虑一个含有元素 [2, 3, 1, 1, 4]
的数组。
- 在索引
0
,你可以跳到1
或2
,所以nextDistance = 2
。 - 到达索引
1
,跳跃可以让你达到4
(最远),所以更新nextDistance = 4
。 - 当遍历到
1
的时候 (此时curDistance = 2
),会增加跳跃次数,并更新curDistance
,显然你不了解它是否为最后的位置,但反复操作可使你重叠到达所有可能的覆盖。