zoukankan      html  css  js  c++  java
  • 小白学算法:买卖股票的最佳时机!

    本文已收录至 Github《小白学算法》系列:https://github.com/vipstone/algorith

    今天蚂蚁集团(支付宝)正式上市了,毫无疑问这一举措又造就了一大批富豪,然而作为局外人的我们,也只有羡慕的份了。明明可以考运气吃饭,咱非得靠实力,你说冤不冤啊?

    但话又说回来,能进蚂蚁的人也都是牛人,那咱也赶紧提升一下技能吧,好为下一个“蚂蚁”做足准备。

    今天的这道题比较有意思,是关于「买卖股票」的,题目如下。

    题目描述

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
    如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

    注意:你不能在买入股票前卖出股票。

    示例 1:

    输入: [7,1,5,3,6,4]

    输出: 5

    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
        注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

    示例 2:

    输入: [7,6,4,3,1]

    输出: 0

    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

    来源:LeetCode

    剑指 offer 64:https://leetcode-cn.com/problems/gu-piao-de-zui-da-li-run-lcof/submissions/
    难度:中

    leetcode 121:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
    难度:简单

    解题思路

    根据题目的意思我们知道,我们只有一次交易的机会,也就是买一次再卖一次,但同时要保证收益最大化。那我们本能的直觉是在最低的价格买入,再在最高的价格卖出就好了,如下图所示:

    image.png

    但这有一个问题,就是我们要保证最高价格要在最低的价格之后,因为我们必须在购买了股票之后才能卖出,而不是相反的顺序,这就让问题变的复杂了。

    但此刻我们想到了一个最直接也是最笨的一个方法,那就是用暴力穷举法,我们使用两层循环,依次在每个节点买入,然后再在之后的所有节点卖出,这样来计算节点间的差值(收益),如果此差值大于当前最高收益,就将此值设置为当前最高收益,这样循环完,我们就能获得最大收益了。如下图所示:

    image.png

    方法一:暴力法

    有了上面的思路,接下来我们用代码实现一下:

    class Solution {
        public int maxProfit(int[] prices) {
            int mp = 0; // 最高收益
            for (int i = 0; i < prices.length; i++) {
                for (int j = i + 1; j < prices.length; j++) {
                    int item = prices[j] - prices[i];
                    if (item > mp) mp = item;
                }
            }
            return mp;
        }
    }
    

    可以看出代码还是很简单的,但别高兴得太早,我们来看它在 leetcode 上的表现:
    image.png

    复杂度分析

    • 时间复杂度:O(n^2),循环运行 n(n-1)/2 次。
    • 空间复杂度:O(1),只使用了常数位的变量。

    真是一顿操作猛如虎,最终击败百分之五!如果用这种代码去面试的话,估计只能回去等通知了。那有没有更好的方法呢?答案是肯定的,继续往下看。

    方法二:遍历一次

    对于这道题我们其实可以使用一次循环来实现,先来看下面的这张折线图:

    image.png

    从上面的图片我们可以看出,我们在每个节点其实只会做两件事(第一个节点除外,只能买入不能卖出),这两件事分别是:买入或卖出。那么我们其实可以用一个循环来计算出最大的利润,我们只需要依次对于每个节点做以下两个判断:

    • 判断当前节点是不是相对最低价,如果是,则将它设置为最低价(也就是买入);
    • 如果当前节点不是最低价,那我们就将它卖出,然后计算卖出的收益(当前节点减去相对最低价),如果卖出的收益大于目前的最高收益,则将此值设置为最高收益。

    这样循环完成之后最高收益就出来了,实现代码如下:

    class Solution {
        public int maxProfit(int prices[]) {
            if (prices == null || prices.length == 0) return 0;
            int mp = 0; // 最高收益
            int min = prices[0]; // 最低价
            for (int i = 1; i < prices.length; i++) {
                if (prices[i] < min) {
                    // 设定相对最低价
                    min = prices[i];
                } else if (mp < (prices[i] - min)) {
                    // 设定最高盈利
                    mp = prices[i] - min;
                }
            }
            return mp;
        }
    }
    

    以上代码在 leetcode 中的结果如下图所示:
    image.png

    复杂度分析

    • 时间复杂度:O(n),只需要遍历一次。
    • 空间复杂度:O(1),只使用了常数个变量。

    从以上的执行的结果可以看出,这段代码还算是比较理想的,这样面试官也会对你竖起大拇指了。

    总结

    本文我们计算了单次(一次买入和卖出)股票的最高收益,刚开始我们使用的是最简单粗暴的暴力枚举法,使用两层循环依次相减来求出最高收益值,但这种方法的执行效率太低。

    然后我们经过观察折线图发现,只需要一次循环也能找出最高的收益值。我们只需要在每个节点做两个判断,第一:判断此节点是否为相对最小值,如果是,则记录下来;如果不是,则计算此值和相对最小值是否为当前最高收益,如果是,则记录下来。那么循环一圈之后,我们就能得出最高的收益了,并且执行的效率也很高。

    你学会了吗?有不懂的地方或者更好的方法,欢迎评论区留言~

  • 相关阅读:
    基于Metaweblog API 接口一键发布到国内外主流博客平台
    uva144 Student Grants
    Uva 10452
    Uva 439 Knight Moves
    Uva 352 The Seasonal War
    switch语句
    java——基础知识
    我的lua学习2
    codeforces 431 D. Random Task 组合数学
    codeforces 285 D. Permutation Sum 状压 dfs打表
  • 原文地址:https://www.cnblogs.com/vipstone/p/13895007.html
Copyright © 2011-2022 走看看