122.买卖股票的最佳时机II 55. 跳跃游戏 45.跳跃游戏II

文章介绍了如何使用贪心算法和双指针技巧解决买卖股票的问题,计算最大利润,以及在跳跃游戏中寻找最少跳跃次数到达终点。通过实例和代码展示了如何在股票价格波动中找到买卖时机和在有限跳跃能力下达到目标的策略。
摘要由CSDN通过智能技术生成

122.买卖股票的最佳时机II

力扣题目链接(opens new window)

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

  • 输入: [7,1,5,3,6,4]
  • 输出: 7
  • 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例 2:

  • 输入: [1,2,3,4,5]
  • 输出: 4
  • 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

  • 输入: [7,6,4,3,1]
  • 输出: 0
  • 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 3 * 10 ^ 4
  • 0 <= prices[i] <= 10 ^ 4

思路:贪心(双指针)

获取股票的最大利润,在最低点买入,最高点售出。而股票的区间是波动的,可能存在多个递增/递减区间
需要找到连续递增区间的起始位置和终止位置,计算连续递增区间的差值.需要至少有两天才能组成一个区间
局部最优价:找到连续递增区间的起始位置和终止位置,计算递增区间的差值.可使用双指针法
整体最优解:得到最大利润
股票可能存在持平的天数,股票的利润大小没有影响,同时会加大计算利润的难度,需要对持平的天数去重。可使用双指针法

时间复杂度 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;

}

55.跳跃游戏

力扣题目链接(opens new window)

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

  • 输入: [2,3,1,1,4]
  • 输出: true
  • 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

示例 2:

  • 输入: [3,2,1,0,4]
  • 输出: false
  • 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

思路:贪心

贪心系列的算法之间没有什么关联,因为贪心算法没有套路,只能多做题目来锻炼自己的贪心思维
当前数组元素为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;

45.跳跃游戏II

力扣题目链接(opens new window)

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

  • 输入: [2,3,1,1,4]
  • 输出: 2
  • 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

说明: 假设你总是可以到达数组的最后一个位置。

思路:贪心

思路:贪心
使用最少的跳跃次数到达数组最后一个元素
那每次跳跃【覆盖范围】要为最大
首先定义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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值