您的位置:首页 > 职场人生

字节算法面试题

2021-08-25 16:39 1046 查看

大家好,今天我们来聊一聊股票交易问题。

Tips:炒股投资的朋友可以直接走了,这是程序员的笔试面试题,不是真正的去探讨炒股哦!不过这两天港股涨的不错...

​ 前几天群里的小伙伴参加字节面试,遇到了股票交易这么一道题。今天我们就来分析一下。同时也给即将要参加校招的朋友们提供准备,这是字节腾讯等大厂校招时常考的题目。

问题描述:

给定一个数组prices,它的第i个元素 prices[i]表示一支给定股票第i天的价格。你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0。

分析问题:

​ 拿到这个问题,我们就需要先思考用什么样的思想去解决。我们来看这个题目,这个问题是求最优解问题。初看题目,我们最容易想到的方法,就是暴力求解,即遍历数组,求出最大值和最小值,相减就是最大利润。不过这里有一个隐含条件,就是股票的卖出时机必须大于股票的买入时机。

def maxProfit(prices):
#最大利润
ans = 0
#股票卖出时机要大于股票买入时机
for i in range(len(prices)):
for j in range(i + 1, len(prices)):
ans = max(ans, prices[j] - prices[i])
return ans

prices=[7, 1, 5, 3, 6, 4]
maxProfit(prices)

​ 我们可以看出,这种求解的方法,时间复杂度是O(n^2),那有没有时间复杂度更优的方法呢?

​ 首先,我们来分析一下这个问题符合什么“问题模型”呢?我们从0开始走,走到n-1,一共需要走n-1步,也就对应着n-1个阶段。每个阶段都只能往右走,并且每个阶段都会对应一个状态集合,我们把状态定义为maxprofit[i],其中i表示走到哪一步,maxprofit[i]的值表示从开始走到位置i的最大利润是多少。所以这个问题是“多阶段决策最优”问题,对于这类问题,我们首先要考虑能否用动态规划的思想来解决,也就是看是否符合动态规划的特征:重复子问题、无后效性、最优子结构。

  1. 无后效性:我们要想计算maxprofit[i]这个状态,只需要关心maxprofit[i-1]的状态,并不关心maxprofit[i-1]这个状态是如何生成的。而且,我们只能往后移动,不允许后退。所以,前面阶段的状态确定之后,不会被后面阶段的决策所改变。所以符合"无后效性"。

  2. 重复子问题:如果要达到maxprofit[i]这个状态,我们可以有不同的决策序列。比如假设第五天,我们的最大利润是8,即maxprofit[i]的状态为8,我们可以是第一天买入2,第三天卖出10。也可以第二天买入2,第三天卖出10。

  3. 最优子结构:因为maxprofit[i]可以通过maxprofit[i-1]推导出来,即符合“最优子结构”

    maxprofit[i]=max(maxprofit[i-1],prices[i]-minprice)

    下面我们看代码如何实现:

def maxProfit(prices):
n=len(prices)
if n==0:
return 0
maxPRofit=[0]*n
minprice=prices[0]

for i in range(1,n):
minprice = min(minprice, prices[i])
maxPRofit[i]=max(maxPRofit[i-1],prices[i]-minprice)

return maxPRofit[-1]

prices=[7, 1, 5, 3, 6, 4]
print(maxProfit(prices))

​ 我们可以看出,这种求解的时间复杂度为O(n)。

###扩展:

​ 我们再把问题复杂一点,假如我们可以进行多次交易一支股票。

给定一个数组 prices,其中 prices[i] 是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

​ 这个问题的解题思路和上题类似,也是“多阶段决策最优”问题。也符合动态规划解题模型。不过这里的“状态”会有所区别,我们来看一下。

​ 考虑到“不能同时参与多笔交易”。所以,每天交易结束后手里只可能存在有股票和没有股票两种状态。所以,我们可以用二个数组来表示,其中dp1[i]表示第i天交易后,手里没有股票的最大利润。dp2[i]表示第i天交易完成后,手里有股票的最大利润。

​ dp1[i]表示第i天手里没有股票的最大利润,那么这个状态要么是由dp1[i-1],即前一天手里没有股票的最大利润,转移过来。要么由dp2[i-1]+prices[i],即前一天手里有股票,今天把这个股票卖出。所以dp1[i]的状态转移方程为:

dp1[i]=max(dp1[i-1],dp2[i-1]+prices[i])

​ dp2[i]表示第i天手里有股票的最大利润,那么这个状态要么是由dp2[i-1],即前一天手里有股票的最大利润,转移过来。要么由dp1[i-1]-prices[i],即前一天手里没有股票,今天把这个股票进行买入。所以dp2[i]的状态转移方程为:

dp2[i]=max(dp2[i-1],dp1[i-1]-prices[i])

​ 所以我们代码实现如下所示:

def maxProfit(prices):
n=len(prices)
if n==0:
return 0
dp1=[0]*n
dp2=[0]*n
#手里没有股票
dp1[0]=0
#手里有股票
dp2[0]=-prices[0]
for i in range(1,n):
dp1[i]=max(dp1[i-1],dp2[i-1]+prices[i])dp2[i]=max(dp2[i-1],dp1[i-1]-prices[i])
return dp1[n-1]

prices=[7, 1, 5, 3, 6, 4]
print(maxProfit(prices))

​ 我们再来把题目升级一下,即我们限制股票买卖的次数,比如我们只能交易两次。

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

​ 这个题目和上个题相比,状态又发生了变化,这里因为限制了交易次数,所以我们需要把交易次数的状态也保存起来。所以每天交易结束后,有5种状态。

  1. 未进行过任何操作。

  2. 只进行过一次买入操作。

  3. 进行过一次买入和卖出操作。

  4. 完成一笔的交易前提下,进行了第二次买入操作。

  5. 完成两笔交易。

    由于第一个状态的利润为0,所以我们不需要记录。我们把剩下的4个状态分别用buy1,sell1,buy2,sell2来表示。下面我们来看一下如何根据前一天的状态,来通过转移方程生成今天的4个状态。

​ 对于buy1而言,我们可以今天不操作,直接通过昨天的buy′ 1转移过来,或者我们从未进行过任何操作前提下,今天买入股票。

​ buy1 =max(buy′ 1,-prices[i])

​ 对于sell1而言,我们今天可以不做任何操作,直接通过昨天的sell′ 1转移过来,或者我们可以在只进行过一次买入交易的前提下,今天把股票卖出。

​ sell1=max(sell′ 1,buy′ 1+prices[i])

​ 对于buy2而言,我们今天可以不做任何操作,直接通过昨天的buy′ 2转移过来,或者,我们在进行过一笔完成的交易条件下,今天再把该股票买入。

​ buy2=max(buy′ 2,sell′ 1-prices[i])

​ 对于sell2而言,我们今天可以不做任何操作,直接通过昨天的sell′ 2 转移过来,或者,我们在buy2的前提下,今天把股票卖出。即

​ sell2=max(sell′ 2,buy′ 2+prices[i])

Tips:我们在计算sell1时,我们可以直接使用buy1而不是buy′ 1进行转移。buy1比buy′ 1多考虑的是在第i天买入股票的情况,而转移到sell1时,考虑的是在第i天卖出股票的情况,这样在同一天买入并且卖出股票的收益为0,不会对结果产生影响。对于buy2和sell2也是同样的考虑,我们可以直接用第i天求出的值来进行转移。

我们来看代码实现: ``` def maxProfit(prices): n = len(prices) #表示以prices[0]买入股票 buy1=-prices[0] #表示在同一天买入并且卖出,即为0 sell1=0 #表示同一天买入卖出后再以prices[0]的价格买入 buy2=-prices[0] #表示同一天买入卖出两次,即为0 sell2 = 0 for i in range(1, n): buy1 = max(buy1, -prices[i]) sell1 = max(sell1, buy1 + prices[i]) buy2 = max(buy2, sell1 - prices[i]) sell2 = max(sell2, buy2 + prices[i]) return sell2 prices=[7, 1, 5, 3, 6, 4] print(maxProfit(prices)) ``` ## 最后 ​ 对于像“多阶段决策最优”问题来说,我们首先要考虑是否能用动态规划来解决,看是否满足动态规划的三个特征:重复子问题、最优子结构、无后效性。然后写出状态转移方程,那问题基本就解决了。 ​ 今天,我们就聊到这里。更多有趣知识,请关注公众号【程序员学长】。我给你准备了上百本学习资料,包括python、java、数据结构和算法等。如果需要,请关注公众号【程序员学长】,回复【资料】,即可得。 ​ 你知道的越多,你的思维也就越开阔,我们下期再见。 ![](https://img2020.cnblogs.com/blog/2453791/202108/2453791-20210825163838269-191897079.png)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: