【算法】贪心算法:LeetCode 53 最大子序和、LeetCode 122 买卖股票的最佳时机 II

LeetCode 53 最大子序和

(简单)

题目

描述
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例1
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:
输入:nums = [1]
输出:1

示例 3:
输入:nums = [0]
输出:0

示例 4:
输入:nums = [-1]
输出:-1

示例 5:
输入:nums = [-100000]
输出:-100000

思路

本题可用贪心的思想解决,局部最优:当前“连续和”为负数的时候⽴刻放弃,从下⼀个元素重新计算“连续和”,因为负数加上下⼀个元素 “连续和”只会越来越⼩。全局最优:选取最⼤“连续和”。本题要求最大的子序列和,而此和的结果又是受到此最大和序列中每个元素的影响,其中有的元素“贡献”自己的力量,有的元素“消耗”集体的力量,同时,我们又不能简单地理解为上面的两类元素分别是正值、负值,因为实际情况中,举一个极端的例子,也可能本序列中的值都为负值,因此,上面所说的“贡献”与“消耗”是相对的,是相对与每次循环时,前一次循环的累积财富值与当前循环中的元素的 贡献/消耗 值之和,与当前元素的值相比,是否能维持其较大的状态,即前者是否能较大,若能,则应继续遍历剩余元素,否则应将当前的较大状态转为当前元素的值。

(补充:之前,本题也用动态规划解决过,若读者有兴趣可去阅读:详细解析:连续子数组的最大和

实现

public class TX3最大子序和 {
    public int maxSubArray(int[] nums) {
        int sum = 0;
        int maxSum = nums[0];

        //for (int i = 0; i < nums.length; i++) {
        //    sum = Math.max(sum + nums[i], nums[i]);
        //    maxSum = Math.max(sum, maxSum);
        //}

        for (int num : nums) {
            sum = Math.max(sum + num, num);
            maxSum = Math.max(sum, maxSum);
        }

        return maxSum;
    }

    public static void main(String[] args) {
        TX3最大子序和 s = new TX3最大子序和();
        System.out.println("s.maxSubArray(new int[]{-2,1,-3,4,-1,2,1,-5,4}) = " + s.maxSubArray(new int[]{-2, 1, -3, 4, -1, 2, 1, -5, 4}));
        System.out.println("s.maxSubArray(new int[]{5,4,-1,7,8}) = " + s.maxSubArray(new int[]{5, 4, -1, 7, 8}));
        System.out.println("s.maxSubArray(new int[]{-2,-1}) = " + s.maxSubArray(new int[]{-2, -1}));
    }
}

本题我用 for i 和 for each 分别提交了一次,发现执行情况中的执行时间相差一倍,

我用 for i 循环时的执行情况:

在这里插入图片描述
我用 for each 循环时的执行情况:
在这里插入图片描述
难道这两种遍历方式对于数组来说还有差别,for i 下标方式获取不也是用哈希函数一次获取吗,于是我便进行了如下测试:

for i 测试

int[] test = new int[100000];
Arrays.fill(test, 1);

long startTime=System.currentTimeMillis(); //获取开始时间

for (int i = 0; i < test.length; i++) {
    System.out.println(i);
}

//for (int i : test) {
//    System.out.println(i);
//}

long endTime=System.currentTimeMillis(); //获取结束时间

System.out.println("程序运行时间: "+(endTime-startTime)+"ms");

在这里插入图片描述
for each 测试

int[] test = new int[100000];
Arrays.fill(test, 1);

long startTime=System.currentTimeMillis(); //获取开始时间

//for (int i = 0; i < test.length; i++) {
//    System.out.println(i);
//}

for (int i : test) {
    System.out.println(i);
}

long endTime=System.currentTimeMillis(); //获取结束时间

System.out.println("程序运行时间: "+(endTime-startTime)+"ms");

在这里插入图片描述
经过多次本地测试,都是 for i 要快些,而对于 LeetCode 官方的执行平台,测试结果正好相反,那么结论便显而易见了,对于此题,我们不能相信 L 方的执行效率。

LeetCode 122:买卖股票的最佳时机 II

(中等)

题目

描述
给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

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

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

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

思路

本题在读懂题意后,可抛去此题目的背景,只考虑数据元素的求得思路,也就是抛去本题的股票题目背景,只去考虑,在一个序列中,规定被减数在减数的右侧,多对这样的数,进行减法运算,结果的和最大是多少

我们可以将每两个数之间作为一个跨度,而不是将某一个较大的区间(即长度大于2的区间)作为一个跨度,例如,对于序列 1 6 7,以两数之间作为一个跨度的话,则组成1 6 一对,6 7 一对,结果分别为 5 1,和为6;而此处以主观判断来说,我们之间以1 和 7 为跨度,结果为 6,也是一样的。原理就是对于一个单调递增的子序列,以跨度为多少进行 f(x右) - f(x左),最终和的结果都是一样的,同样的还有下图这个例子:

在这里插入图片描述
对于下图的例子,因为我们已经将跨度设定为两个数之间了(也就是紧邻的两个数)那么每个子序列(长度为2)不是单调递增,就是单调递减,因此,我们求最终和最大,只需要将所有递增的序列的差一个个求出来,最终的和,就是答案。

因此,这种贪心思想的局部最优就是每个递增的序列的正数差,全局最优就是所有正数差的和。

在这里插入图片描述

实现

public class TX4买卖股票的最佳时机II {
    public int maxProfit(int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }

        int profit = 0;
        int sum = 0;

        for (int i = 0; i < prices.length - 1; i++) {
            profit = prices[i + 1] - prices[i];
            if (profit > 0) {
                sum += profit;
            }
        }

        return sum;
    }
}

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超周到的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值