目录
理论基础
局部最优推出整体最优
决策不会影响到之前的状态,即某个状态一旦确定,则不受后续步骤的影响
121.股票
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
//股票买卖一次,贪心的想法取最左最小值,取最右最大值,差值就是最大利润
class Solution {
public int maxProfit(int[] prices) {
int low = Integer.MAX_VALUE;
int res = 0;
for(int i = 0; i < prices.length; i++){
low = Math.min(prices[i], low);
res = Math.max(prices[i] - low, res);
}
return res;
}
}
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 , 所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 105
每次取最大跳跃步数(取最大覆盖范围)
整体最优解:最后得到整体最大覆盖范围,看是否能到终点
class Solution {
public boolean canJump(int[] nums) {
int maxPos = 0;//到当前位置为止,能够跳到的最远位置
for (int i = 0; i < nums.length; i++) {
if (i <= maxPos) maxPos = Math.max(maxPos, i + nums[i]);//可以从前面的位置跳到当前位,当前位置+跳数>最远位置,就更新
if (maxPos >= nums.length - 1) return true;//可以到达最后一个位置
}
return false;
}
}
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
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 1000
题目保证可以到达 nums[n-1]
每次取最大跳跃步数(取最大覆盖范围)
整体最优解:最后得到整体最大覆盖范围,看是否能到终点
class Solution {
public int jump(int[] nums) {
int res = 0;// 跳跃次数
int cur = 0;//当前cover
int next = 0;//下一步cover
// 遍历,直到超过当前能跳到的最远位置,且cover在数组内
for (int i = 0; i <= cur && cur < nums.length - 1; ++i) {
next = Math.max(next, i + nums[i]);// 更新 next
if (i == cur) { //到达最远位置时,更新 cur 到 next,并增加跳跃次数
cur = next;
res++;
}
}
return res;
}
}
736.划分字母
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
示例 2:
输入:s = "eccbbbbdec"
输出:[10]
提示:
1 <= s.length <= 500
s 仅由小写英文字母组成
从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
class Solution {
public List<Integer> partitionLabels(String S) {
List<Integer> list = new LinkedList<>(); // 存放每个分区的长度
int[] edge = new int[26]; // 记录每个字母最后出现的位置
char[] chars = S.toCharArray(); // 将字符串转换为字符数组
// 第一步:记录每个字母最后出现的位置
for (int i = 0; i < chars.length; i++) {
edge[chars[i] - 'a'] = i; //减去'a'来计算当前字母的相对位置
}
// 第二步:遍历字符串,根据字母的最后出现位置来分区
int idx = 0; // 当前分区的最远边界
int last = -1; // 上一个分区的结束位置
for (int i = 0; i < chars.length; i++) {
idx = Math.max(idx, edge[chars[i] - 'a']); // 更新当前分区的最远边界
if (i == idx) { // 当前索引等于分区的最远边界时,表示一个分区结束
list.add(i - last); // 计算分区长度并添加到列表
last = i; // 更新上一个分区的结束位置
}
}
return list;
}
}