我们只需遍历一次数组,实时更新当前的最小价格和最大利润。这是一个典型的贪心策略,因为我们总是试图通过当前的选择来尽可能获取最大利益。
贪心算法是一种设计算法的策略,其核心思想是每一步都做出当前看起来最优的选择,以期通过一系列的局部最优解最终达到全局最优解。贪心算法的基本原则是:
- 选择:在每一步中选择当前最优的选项。
- 可行性:确保每一步的选择都是可行的,即能够满足问题的约束条件。
- 局部最优:每一步选择的最优解不会影响后续步骤的选择。
- 全局最优:通过局部最优的选择,能够达到全局最优的解。
题目一、买卖股票的最佳时机
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
C++核心代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
//检查数组是否为空
if(prices.empty()) return 0;
int minprice = prices[0];
int maxProfit = 0;
for(int i=1;i<prices.size();i++){
int currentprice = prices[i];
int currentProfit = currentprice - minprice;
maxProfit = std::max(maxProfit,currentProfit);
minprice = std::min(minprice,currentprice);
}
return maxProfit;
}
};
题目二、跳跃游戏--是否可以跳到最后
给你一个非负整数数组 nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true
;否则,返回 false
。
C++核心代码:
class Solution {
public:
bool canJump(vector<int>& nums) {
int farthest = 0;
for(int i =0;i<nums.size();i++){
if(i>farthest){
return false;
}
farthest=std::max(farthest,i+nums[i]);
if(farthest >= nums.size()-1){
return true;
}
}
return false;
}
};
代码解释
-
初始化:
farthest
变量表示当前能到达的最远位置,初始化为 0。
-
遍历数组:
- 遍历数组的每个下标
i
。 - 如果当前下标
i
超出了farthest
,说明从起始位置无法到达该位置,返回false
。 - 更新
farthest
为i + nums[i]
和现有farthest
中的最大值。 - 如果
farthest
达到或超过最后一个下标n - 1
,返回true
。
- 遍历数组的每个下标
-
返回:
- 如果遍历完成后无法到达最后一个下标,返回
false
。
- 如果遍历完成后无法到达最后一个下标,返回
这个贪心算法的时间复杂度是 O(n),空间复杂度是 O(1),因为我们只用常量级别的额外空间来存储 farthest
变量。
题目三、跳跃游戏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]
。
C++核心代码:
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums.size();
if(n <= 1) return 0;
int jumps = 0;
int currentEnd = 0;
int farstest = 0;
for(int i = 0;i<n-1;++i){
farstest = std::max(farstest,i+nums[i]);
if(i == currentEnd){
++jumps;
currentEnd = farstest;
if(currentEnd >=n-1){
return jumps;
}
}
}
return -1;
}
};
代码解释
-
初始化:
jumps
:记录跳跃次数,初始化为0
。currentEnd
:当前跳跃范围的终点。farthest
:能到达的最远位置。
-
遍历数组:
- 遍历数组中的每个位置
i
,更新farthest
为当前能到达的最远位置(i + nums[i]
)。 - 如果当前索引
i
达到currentEnd
(即当前跳跃范围的终点),则需要增加跳跃次数,并更新currentEnd
为farthest
。 - 如果更新后的
currentEnd
能到达最后一个下标n - 1
,则返回当前的跳跃次数jumps
。
- 遍历数组中的每个位置
-
返回结果:
- 如果遍历结束后,能到达最后一个位置,则返回
jumps
。理论上不应该到达-1
,因为题目保证可以到达最后一个位置。
- 如果遍历结束后,能到达最后一个位置,则返回
题目四、 划分字母区间
给你一个字符串 s
。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s
。
返回一个表示每个字符串片段的长度的列表。
C++核心代码
class Solution {
public:
vector<int> partitionLabels(string s) {
std::unordered_map<char, int>lastPosition;
int n = s.size();
for(int i=0;i<n;++i){
lastPosition[s[i]]=i;
}
std::vector<int> result;
int start = 0;
int end = 0;
for(int i= 0;i<n;++i){
end = std::max(end,lastPosition[s[i]]);
if(i == end){
result.push_back(end-start+1);
start = i+1;
}
}
return result;
}
};
代码解释
-
记录字符的最后出现位置:
- 遍历字符串
s
,并将每个字符的最后出现位置存储在lastPosition
哈希表中。
- 遍历字符串
-
划分片段:
- 遍历字符串
s
,维护一个end
变量表示当前片段的结束位置。 - 对于每个字符,更新
end
为当前字符的最后出现位置(使用std::max
)。 - 当遍历到位置
i
等于end
时,表示当前片段结束。记录片段的长度并更新start
为下一个片段的起始位置。
- 遍历字符串
-
返回结果:
- 将每个片段的长度存储在
result
向量中,并最终返回该向量。
- 将每个片段的长度存储在