难度标识:
⭐:简单,⭐⭐:中等,⭐⭐⭐:困难。
tips:这里的难度不是根据LeetCode难度定义的,而是根据我解题之后体验到题目的复杂度定义的。
1.买卖股票的最佳时机 ⭐
思路
这个问题的核心思路是找到最低的买入价格和最高的卖出价格,同时确保卖出的日期在买入日期之后。
为了实现这一目标,我们可以遍历股票价格数组,对于每一天的价格:
-
更新最低买入价格:如果今天的价格低于我们之前看到的最低价格,那么就更新最低买入价格。这代表了一个新的可能的买入日期。
-
计算可能的利润:用今天的价格减去最低的买入价格,这样我们可以得到如果今天卖出的话可能获得的利润。
-
更新最大利润:比较今天可能获得的利润和我们之前计算的最大利润,取其中的较大值。
遍历完成后,我们就能得到可能的最大利润。
代码
var maxProfit = function (prices) {
let maxVal = 0, minPrice = prices[0]
for (let num of prices) {
minPrice = Math.min(minPrice, num)
maxVal = Math.max(maxVal, num - minPrice)
}
return maxVal
};
2.跳跃游戏 ⭐
思路
解这题的核心思路:
-
起点到最远距离:开始时,你可以跳的最远距离就是第一个数字。
-
逐步更新:当你遍历数组时,每到一个新位置,都尝试看从当前位置能跳到的最远距离,然后与之前的最远距离进行比较,取其中的最大值作为新的最远距离。
-
检查中途是否卡住:在遍历的过程中,如果某一个位置超过了你当前的最远距离,那说明从起点到当前位置的任何路径都无法到达,所以就返回无法到达的结果。
-
检查是否到达终点:如果在遍历过程中,你的最远距离已经超过或等于了数组的最后一个位置,那就意味着你可以到达数组的最后,返回可以到达的结果。
想象你在一个跑道上,你的目标是跑到终点,但你的每一步都有一个最大距离,你会尽量利用每一步走得更远。如果中途发现前方有一个位置,你无论如何都走不到,那就说明你无法到达终点;但如果你一直能走下去,并且超过了终点,那说明你可以到达终点。
代码
var canJump = function (nums) {
let maxPos = 0
for (let i = 0; i < nums.length; i++) {
if (i > maxPos) {
return false
}
maxPos = Math.max(maxPos, i + nums[i])
}
return maxPos >= nums.length - 1
};
3.跳跃游戏 II ⭐
思路
解这题的核心思路:
-
局部最优选择:在当前位置,你有多种跳跃选择。你应该选择那一个跳跃,使得从那个位置开始,你能够跳得最远。这是每一步的局部最优选择。
-
维护两个范围:
-
当前跳跃的范围:表示在当前步骤内,你可以跳跃到的所有位置。
-
下一跳跃的最远范围:在当前跳跃范围内的每一个位置,你都有一个最远可以跳到的位置。其中最大的那个位置,就是下一跳跃的最远范围。
-
-
遍历并更新范围:遍历当前跳跃范围内的每一个位置,找到其中的下一跳跃的最远范围。当遍历完当前范围后,下一跳跃的最远范围就成为新的当前范围。
-
跳跃次数:每次更新当前范围时,跳跃次数加一。
当当前范围到达或超过数组的最后一个位置时,停止遍历。到此为止的跳跃次数就是最小跳跃次数。
这种贪心策略的优点是,它始终确保你每次跳跃都是最有希望到达最远位置的跳跃,因此可以保证得到最小的跳跃次数。
代码
var jump = function (nums) {
let maxPos = 0, count = 0, end = 0
for (let i = 0; i < nums.length - 1; i++) {
maxPos = Math.max(maxPos, i + nums[i])
if (i === end) {
count++
end = maxPos
}
}
return count
};
4.划分字母区间 ⭐
思路
解这题的核心思路:
-
寻找每个字符的最后一次出现的位置:首先遍历整个字符串,记录下每个字符最后一次出现的位置。
-
贪心地分片:从左到右遍历字符串。为当前片段找到结束位置:
-
开始时,片段的结束位置就是当前字符最后出现的位置。
-
在当前片段内,如果遇到的字符的最后出现位置比片段的当前结束位置还要远,那么就更新片段的结束位置。
-
当遍历到片段的结束位置时,记录下片段的长度,并开始寻找下一个片段。
-
-
重复上述步骤:直到遍历完整个字符串。
为什么这个方法有效呢?
当我们确定一个片段的结束位置时,意味着在这个片段之前的任何位置都不可能找到一个更远的结束位置,因为我们总是贪心地选择最远的结束位置。所以,这个方法可以保证我们得到的分片是尽可能多的。
以下是一个简化的步骤说明:
-
先预处理字符串,得到每个字符最后一次出现的位置。
-
从头开始遍历字符串,初始化片段的结束位置。
-
在片段内,更新片段的结束位置。
-
当到达片段的结束位置时,切下片段,并继续寻找下一个片段。
这样,你就可以得到一个表示每个字符串片段的长度的列表了。
代码
var partitionLabels = function (s) {
let obj = {}, res = [], start = 0, end = 0
for (let i = 0; i < s.length; i++) {
obj[s[i]] = i
}
for (let i = 0; i < s.length; i++) {
end = Math.max(end, obj[s[i]])
if (i === end) {
res.push(end - start + 1)
start = i + 1
}
}
return res
};
leetcode热题100中,做过的那么多类题目,我觉得贪心算法类的题目是最简单的,好理解,代码量又少。