力扣题目链接(opens new window)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
示例 2:
示例 3:
提示:
获取股票的最大利润,在最低点买入,最高点售出。而股票的区间是波动的,可能存在多个递增/递减区间
需要找到连续递增区间的起始位置和终止位置,计算连续递增区间的差值.需要至少有两天才能组成一个区间
局部最优价:找到连续递增区间的起始位置和终止位置,计算递增区间的差值.可使用双指针法
整体最优解:得到最大利润
股票可能存在持平的天数,股票的利润大小没有影响,同时会加大计算利润的难度,需要对持平的天数去重。可使用双指针法时间复杂度 O(N)
空间复杂度 O(1)
代码如下
public static void main(String args[]) {
int[] prices = new int[]{3, 3, 5, 0, 0, 3, 1, 4};
maxProfit(prices);
}
public static int maxProfit(int[] prices) {
int profit = 0;// 利润
if (prices == null || prices.length == 0 || prices.length == 1)// 边缘情况校验
return profit;
int slow = 0;
int fast = 1;
while (fast < prices.length) {// 数组相邻元素去重
if (prices[slow] != prices[fast]) {
slow++;
prices[slow] = prices[fast];
}
fast++;
}
int priceLength = slow + 1;
slow = 0;
fast = 1;
while (fast < priceLength) {// 数组连续递增子序列求差值之和
if (prices[fast] > prices[fast - 1]) {
fast++;
if (fast == priceLength)
profit = profit + prices[fast - 1] - prices[slow];
} else {
profit = profit + prices[fast - 1] - prices[slow];
slow = fast;
fast++;
}
}
return profit;
}
双指针法太长时间没用了,不熟练了
其中双指针法对数组连续元素去重的代码写的很复杂
复杂代码
int slow = 0;
int fast = 1;
while (fast < prices.length) {// 数组相邻元素去重
if (prices[slow] != prices[fast]) {
slow++;
fast++;
} else {
fast++;
while (fast < prices.length) {
if (prices[slow] != prices[fast]) {
slow++;
prices[slow] = prices[fast];
break;
} else {
fast++;
}
}
}
}
简化代码
int slow = 0;
int fast = 1;
while (fast < prices.length) {// 数组相邻元素去重
if (prices[slow] != prices[fast]) {
slow++;
prices[slow] = prices[fast];
}
fast++;
}
上述的思路是找到递增子区间,求递增子区间的差值
其中差值可被分解
假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]
假如第0天买入,第1天卖出.假如第1天买入,第2天卖出.假如第2天买入,第3天卖出.
那么利润为:(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。
相当于prices[3] - prices[0]
此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑!
每天的利润有正有负,只需要收集正利润差值
时间复杂度 O(N)
空间复杂度 O(1)
代码如下
public static int maxProfit(int[] prices) {
int profit = 0;// 利润
if (prices == null || prices.length == 0)
return profit;
for (int i = 1; i < prices.length; i++) {
profit = profit + Math.max(0, prices[i] - prices[i - 1]);
}
return profit;
}
力扣题目链接(opens new window)
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
示例 2:
贪心系列的算法之间没有什么关联,因为贪心算法没有套路,只能多做题目来锻炼自己的贪心思维
当前数组元素为3,我究竟是跳一步还是两步还是三步呢
此题目的关键不在于跳几步,而在于跳的范围。
跳的范围能不能覆盖到终点
每次取覆盖范围内元素的值,如果跳跃后大于当前覆盖范围,则更新,否则不更新覆盖范围
最后判断覆盖范围能否到达数组末尾
贪心算法局部最优解:取当前覆盖范围内最大的覆盖范围
贪心算法整体最优解:取最大的覆盖范围
时间复杂度O(n)
空间复杂度O(1)
代码如下
public static void main(String args[]){
int[] nums = new int[]{2,3,1,1,4};
canJump(nums);
}
public static boolean canJump(int[] nums) {
if (nums == null || nums.length == 0)
return false;
int covert = nums[0];// 覆盖范围
for (int i = 0; i < nums.length; i++){
if(covert >= nums.length-1)// 覆盖范围覆盖终点
return true;
if(i > covert)// 覆盖范围没有覆盖终点
break;
if(i + nums[i] > covert)
covert = nums[i] + i;
}
return false;
}
在设置终止条件时有误
错误代码:只是考虑到覆盖范围刚刚好等于到达数组末尾,但覆盖范围大小有可能会超出数组末尾
if(covert == nums.length-1)// 覆盖范围覆盖终点
return true;
正确代码
if(covert >= nums.length-1)// 覆盖范围覆盖终点
return true;
力扣题目链接(opens new window)
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
说明: 假设你总是可以到达数组的最后一个位置。
思路:贪心
使用最少的跳跃次数到达数组最后一个元素
那每次跳跃【覆盖范围】要为最大
首先定义for循环对数组元素遍历
不断更新覆盖范围covert = Math.max(nums[i] + i, covert);
但要注意更新count的时机,只有将覆盖范围preCovert的数组元素遍历完成后,才可以更新count
代码如下
public static void main(String args[]) {
int[] nums = new int[]{7, 0, 9, 6, 9, 6, 1, 7, 9, 0, 1, 2, 9, 0, 3};
jump(nums);
}
public static int jump(int[] nums) {
if (nums == null || nums.length == 0 || nums.length == 1)
return 0;
int count = 0;// 跳跃次数
int covert = 0; // 跳跃的覆盖范围
int maxCovert = 0; // 跳跃的最大覆盖范围
for (int i = 0; i < nums.length; i++) {
covert = Math.max(nums[i] + i, covert);// 更新覆盖范围
if (i == maxCovert) {// 遇到当前覆盖范围最远范围
count++;
maxCovert = covert;
if (maxCovert >= nums.length - 1)
break;
}
}
return count;
}