Description
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: 5max. 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: 0In this case, no transaction is done, i.e. max profit = 0.
sln1
阅读题目后,马上能想到的解决方案就是用两层循环,计算 max(∑i,jprices[j]−prices[i]) 其中 i∈[0,n),j∈(i,n) ,n为prices数组长度。不难分析得到该算法的复杂度为O(n^2)。用另个循环实现了一下后提交发现超时了,有一个输入样例是从9000到0的倒叙,所以要计算 90002 次,显然不现实。
sln2
有没有什么办法可以遍历一次prices数组就获取到最大的profit的呢?思考一下我们可以想出来,对于任意一个价格i,最大的profit要么就是在prices[i] - min(prices[:i - 1]),要么就是max_profit(prices[:i-1])。其中min(prices[:i - 1])是指i价格以前遇到的最小的价格,而max_profit(prices[:i-1])则是指i价格以前的最高收益。那么,我们只要简单记录遍历到i价格时,遇到的最小价格以及遇到的最大profit就可以以O(n)的时间获取到整个prices数组中能求得的最大profit了。python实现如下:
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
if len(prices) < 1:
return 0
min_price = prices[0]
max_profit = max(0, -prices[0])
for i in xrange(1, len(prices)):
min_price = min(min_price, prices[i])
max_profit = max(max_profit, prices[i] - min_price)
return max_profit
sln3
实现完上面的算法后感觉到O(n)了还是挺满意的,于是开始照管理翻看discussion中大牛们的解法。解法都比较大同小异,只有一个觉得比较有趣。该解法将此问题转换成一个求和最大的连续子数组的问题。具体如何转换呢,我们用Example 1 里的例子举例:
prices: [7, 1, 5, 3, 6, 4]
diff: [0, -6, 4, -2, 3, -2]
其中的diff数组很容易看出来,是prices[i] - prices[i - 1],也就代表在第i - 1天买入股票再在i天卖出后的profit。而diff[0] = 0是因为第0天我们并未持有股票,因此无法卖出。所以profit为0。那么为什么我们要求diff的和最大的连续子集呢?我们现在已经知道了当输入为上面prices数组时,获得最大profit的情况下我们在i = 1时买,i = 4时卖。同样,我们也可以看成,我们在i=1时开始买,i=2时卖了再买,i=3时卖了再买,i=4时再卖(-1 + 6 = -1 + 5 - 5 + 3 - 3 + 6)。也就是说,diff子串的和的物理意义就是在子串的第一个元素下标i买入,再在子串的最后一个元素下标j卖出后得到的profit(prices[j] - prices[i])。那么我们要求 max(∑i,jprices[j]−prices[i]) ,也就等同于求diff数组的最大和连续子串。求最大和连续子串有很多不同的方法,discussion中的大牛用了一个叫做Kadane’s Algorithm的算法,虽然我之前也没怎么听过,但是还是比较好理解的。就是用两个变量来记录,到当前遍历的下标i之前,和最大的连续子数组的和(maxSoFar),以及包含i的最大和连续子数组的和(maxCur)。Java实现如下:
public class Solution {
public int maxProfit(int[] prices) {
int maxCur = 0, maxSoFar = 0;
for(int i = 1; i < prices.length; i++) {
maxCur = Math.max(0, maxCur += prices[i] - prices[i-1]);
maxSoFar = Math.max(maxCur, maxSoFar);
}
return maxSoFar;
}
}