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 (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Note that you cannot sell a stock before you buy one.
Example 1:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 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
Explanation: In this case, no transaction is done, i.e. max profit = 0.
这道题目的意思是根据股票价格的走势,求买卖一次所获得的最大收益(先买后卖)。我开始用了两层for循环来遍历出最大值:
class Solution {
public int maxProfit(int[] prices) {
int maxProfit = 0;
for(int i=0;i<prices.length-1;i++)
for(int j=i+1;j<prices.length;j++)
if(prices[j]-prices[i]>maxProfit){
maxProfit=prices[j]-prices[i];
}
return maxProfit;
}
}
由于时间复杂度太高O(n^2),耗时377ms。在讨论区又找到一种堪称神奇的算法:Kadane’s Algorithm,其代码简洁优美且时间复杂度为O(n)。这个算法是用来求最大子列和的问题,而本题目又恰好可以转为求最大子列和的问题。
Kadane’s 算法求最大子列和:
Kadane’s 算法运用了数学归纳法的思想,对一个数列,比如:{1,-2,6,-4,1,3,-1} 想知道它的最大子列看似计算很复杂,但我们可以把它化繁为简,从长度为1的子列开始,求最大子列和。知道了长度为1的最大子列和,那长度为2的就也知道了,依次类推。从数组第一个数开始,随着数组往后数的增加,保持对最大子列和的监控。
数列长度i+1的最大子列和 = max(以第i+1个数结尾的子列和, 数列长度i的最大子列和);
以第i+1个数结尾的子列和 = max(x,以第i个数结尾的子列和+x);
附上wikipedia对Kadane’s Algorithm的解释:
A bit of a background: Kadane’s algorithm is based on splitting up the set of possible solutions into mutually exclusive (disjoint) sets. It exploits the fact that any solution (i.e., any member of the set of solutions) will always have a last element i (this is what is meant by “sum ending at position i”). Thus, we simply have to examine, one by one, the set of solutions whose last element’s index is 1, the set of solutions whose last element’s index is 2, then 3, and so forth to n. It turns out that this process can be carried out in linear time.
Kadane’s algorithm begins with a simple inductive question: if we know the maximum subarray sum ending at position i (call this B_{i}), what is the maximum subarray sum ending at position i+1 (equivalently, what is B_{{i+1}})? The answer turns out to be relatively straightforward: either the maximum subarray sum ending at position i+1 includes the maximum subarray sum ending at position i as a prefix, or it doesn’t (equivalently, B_{i+1}=max(A_{i+1},A_{i+1}+B_{i}), where A_{i+1} is the element at index i+1).
Thus, we can compute the maximum subarray sum ending at position i for all positions i by iterating once over the array. As we go, we simply keep track of the maximum sum we’ve ever seen.
演示一遍代码或许更好理解:
def max_subarray(A):
max_ending_here = max_so_far = A[0]
for x in A[1:]:
max_ending_here = max(x, max_ending_here + x)
max_so_far = max(max_so_far, max_ending_here) #对最大子列和的监控
return max_so_far
Note: with a bit of reasoning you will see that max_so_far is equal to max(B_{0},B_{1},B_{2},…,B_{i})
The algorithm can also be easily modified to keep track of the starting and ending indices of the maximum subarray (when max_so_far changes) as well as the case where we want to allow zero-length subarrays (with implicit sum 0) if all elements are negative.
当数组元素全为负数时,允许子列长度为0,默认最大子列和为0。
再回到原题目:我们可以构造新数组price[1]-prices[0],price[2]-prices[1],…,price[n]-prices[n-1] 求此数组的最大子列和就等于求原题目中的最大收益。
class Solution {
public int maxProfit(int[] prices) {
int max_ending_here,max_so_far;
max_so_far=max_ending_here=0;
for(int i=1;i<prices.length;i++){
max_ending_here=Math.max(max_ending_here+prices[i]-prices[i-1],prices[i]-prices[i-1]);
max_so_far=Math.max(max_so_far,max_ending_here);
}
return max_so_far;
}
}
这里max_so_far=max_ending_here=0是为了满足Note数组元素全为负数的情况。耗时1ms左右,大大提高了性能。
参考链接(Kadane’s Algorithm):
https://blog.youkuaiyun.com/the__apollo/article/details/77367534
“尽力做到不要让他人旧事扰乱自己的学习与生活。”
———喵,就是这样