(leetcode刷题)Best Time to Buy and Sell Stock

来源:互联网 发布:discuz阿里云cdn设置 编辑:程序博客网 时间:2024/05/23 19:59

题目要求我们在一个代表着股票价格时间序列的数组中,找到最佳的买点和卖点,从而求出利润最大额是多少。
本质是找到数组中元素的最大差(但是要保证被减数在减数的右边),如果差值都是负数(怎么买都亏本),那就输出0(不买了)

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Example 1:

Input: [7, 1, 5, 3, 6, 4]
Output: 5

max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)
Example 2:

Input: [7, 6, 4, 3, 1]
Output: 0

In this case, no transaction is done, i.e. max profit = 0.

最容易想到的是O(N^2)的方法,穷举所有情况,但是这样会超时。
一种简单的改进方法是,在找下一个左边的起点的时候,看后面是不是有比它更小或一样的值,有的话就直接跳过。这样就可以通过用例了。

class Solution {public:    int maxProfit(vector<int>& prices) {        int i = 0 , maximal = 0;        while(i < prices.size()){            while(i+1<prices.size() && prices[i+1]<=prices[i]){                ++i;            }            for(int j = i+1; j < prices.size(); ++j){                maximal = max(maximal,prices[j] - prices[i]);            }            ++i;        }        return maximal;    }};

但是这样效率依然不高,还是重复遍历了多次,所以还可以改进
我们发现只要一次遍历,在从左往右遍历的时候保存住左边最低价,然后随着遍历的推进,看能不能减最低价得出等多的利润。
因为从左往右遍历,所以卖的点必然可以保证在买的点后面。

class Solution {public:    int maxProfit(vector<int>& prices) {        int profit = 0 , buy = INT_MAX;        for(int i = 0; i < prices.size(); ++i){            buy = min(prices[i],buy);            profit = max(prices[i]-buy,profit);        }        return profit;    }};

接下来看该题的加强版Best Time to Buy and Sell Stock II
这次问题被改为,可以进行多次的买和卖(但不能在同一点又卖又买)
Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

其实只要在第一题的基础上稍做改变即可:
即把利润又原来的求最大值改为累加值,然后每进行完一次交易之后,把buy做一个更新(只找这之后的最小值,因为前面已经卖了,找买点就只能在后面找了)。

class Solution {public:    int maxProfit(vector<int>& prices) {        int profit = 0 , buy = INT_MAX;        for(int i = 0; i < prices.size(); ++i){            buy = min(buy,prices[i]);            if(prices[i] - buy > 0){                profit += prices[i] - buy;                buy = prices[i];            }        }        return profit;    }};

最后是此题的第三个版本Best Time to Buy and Sell Stock III
题目说这次可以买卖两次,让我们求最大利润。

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

因为是买卖两次,所以我们可以从左往右求出每个下标位置的最大利润值,再从右往左求一次,分别保存在两个数组中。
然后找到同时遍历两个数组,找到一个下标,在这个下标上从左往右的最大利润加上从右往左的最大利润之和最大,就是我们要求的结果。(假设是下标i,那意思就是说0~i之间做了最多一次买卖取得的利润+i~n之间可能发生的第二次买卖取得的利润)

PS:从左往右和从右往左的两次求最大利润的方法其实就是动态规划(如果当前节点产生了比之前更大的利润,那就记录下这个利润,否则照搬之前的利润)
下面是代码:

class Solution {public:    int maxProfit(vector<int>& prices) {        if(prices.size() <= 1){            return 0;        }        vector<int> preProfit(prices.size(),0);        vector<int> postProfit(prices.size(),0);        int buy = prices[0];        int sell = prices[prices.size()-1];        //因为是动态规划的递推,所以不从0开始,0作为了初始状态        for (int i = 1; i < prices.size(); ++i) {            buy = min(prices[i], buy);            preProfit[i] = max(prices[i] - buy , preProfit[i-1]);        }        for (int i = prices.size() - 2; i >= 0; --i) {            sell = max(prices[i], sell);            postProfit[i] = max(sell - prices[i] , postProfit[i+1]);        }        int profit = 0;        for (int i = 0; i < prices.size(); ++i) {            profit = max(profit, preProfit[i] + postProfit[i]);        }        return profit;    }};
0 0