一道小面试算法题的思路
一道小算法题的思路
有这么一道小面试算法题:给定一个长度为 n 的整数数组,下标为 i 的元素表示第 i 天某个股票的价格,每次最多持有一股,每次买卖最多一股,在最多只买卖一次的情况下(先买后卖,不考虑融券等复杂形式),最大的收益是多少。
先考察一下可能的数据:
0 1 2 3 4 5 6 7
12 9 6 10 8 22 20 15
由于是先买后卖,从数据上看,是在 2 处买入,5 处卖出时,得到收益为 16 最大。
而在股票一直下跌的情况下,不买不卖,得到的收益为 0 最大。
-
o(n^2)
直观算法最简单的思路,就是从前向后逐个计算收益,求最大收益,如下:
price[n] max_profit = 0 for i = 0...n-2: for j = i+1...n-1: profit = price[j] - price[i] if profit > max_profit: max_profit = profit
这个算法的复杂度为
o(n^2)
,能不能继续优化? -
o(n)
算法用归纳法分析一下,在第 n-1 天时,最大收益为 max_profit ,其中最小的价格为 min_price,那么第 n 天时,最大的收益计算为:
if price[n] - min_price > max_profit: max_profit = price[n] - min_price
因此,可把算法改为:
price[n] max_profit = 0 min_price = price[0] for i = 1...n-1: profit = price[i] - min_price if profit > max_profit: max_profit = profit if min_price > price[i]: min_price = price[i]
变化为
o(n)
复杂度。 -
推广变化
现在,考虑更加复杂的形式,先来个允许不限次数的买卖,那么,在每一个递增的子区间,都能得到收益,总收益为各个收益的和:
price[n] sum_profit = 0 for i = 1...n-1: profite = price[i] - price[i-1] if profite > 0: sum_profit += profit
再复杂一点,允许进行融券形式(即允许从券商借出后先卖后买),同样限制最多持有或融入 1 股,在结束前使得手上股票数为 0 。这种规则下,股票价格升降都会得到收益,总收益为:
price[n] sum_profit = 0 for i = 1...n-1: profite = price[i] - price[i-1] if profite < 0: sum_profite += -profite else: sum_profite += profite
在此基础上,还可以放开一些限制条件,如不限制持有的数量等等。更进一步,可设定一个收益目标,符合收益目标的买卖组合,这样就更加现实意义有意思了。