经典的面试问题是利润最大化,一次交易,允许n次交易和k次交易来购买股票。
有人问我一个类似的问题,但有一个扭曲的约束: 您可以多次购买股票(每天最多可以购买一个单位),但是在售出股票后就不能购买。
这有一个引理,您只能卖一次。
例如: 70 40 90 110 80 100
选项1:B B B卖出_ _ = 130
选项2:B B B X B Sell = 120
较旧的问题
https://www.geeksforgeeks.org/stock-buy-sell/
https://www.geeksforgeeks.org/maximum-profit-by-buying-and-selling-a-share-at-most-twice/
https://www.geeksforgeeks.org/maximum-profit-by-buying-and-selling-a-share-at-most-k-times/
Stackoverflow讨论
maximizing profit for given stock data via DP
Maximizing profit for given stock quotes
Maximum profit by buying and selling a share exactly k times
答案 0 :(得分:3)
可以使用BST在O(n lg n)
空间O(n)
中解决此问题。
class BSTNode{
BSTNode(val){
this.val = val
this.sum = sum
this.left = this.right = null
this.count = 1
}
insert(val){
if(val < this.val)
insert val into the left subtree
else if(val > this.val)
insert val into the right subtree
this.sum += val
this.count++
}
count_sum_nodes_upper_bound(val){
if(val < this.val)
return this.left.sum_nodes_lower_bound(val)
else if(val == this.val)
return this.left.sum, this.left.count
else{
right_sum, right_count = this.right.sum_nodes_lower_bound(val)
return (this.sum - this.right.sum + right_sum),
(this.count - this.right.count + right_count)
}
}
}
以上只是正确的BST外观的粗略概述。实际上,您可能想使用平衡树,并检查count_sum_nodes_lower_bound
中是否存在子树。上面的代码解释了:
每个BSTNode
除了BST的标准属性外,还具有属性sum
和count
,其中count
是子树中的元素数,{ {1}}是其中所有元素的总和。
Insert的工作方式与普通BST中的工作方式相同,只是在每个节点中需要更新相应的sum
和sum
。如果多次插入相同的值,count
和sum
将被更新以反映重复性。
然而,中心部分是方法count
,该方法计算给定上限的元素数及其和。对于给定的上限count_sum_nodes_upper_bound
,在值b
的节点上可能发生三种情况:
v
:所有相关元素都包含在左子树中b < v
:左侧子树是查询结果b == v
:左子树和当前节点中的所有值都是该子集的一部分。此外,右侧子树的某些节点也是结果的一部分,我们需要通过递归找到。借助此BST,我们现在可以轻松找到上述问题的解决方案:
b > v
上面的代码根据BST中存储的值找到最佳销售日期的索引。在迭代maximize_profit(A){
node = BSTNode(A[0])
max = 0
max_idx = -1
for(i in 1..(A.length - 1)){
sum, count = node.count_sum_nodes_upper_bound(A[i])
gain = A[i] * count - sum
if(max < gain){
max = gain
max_idx = i
}
node.insert(A[i])
}
return max_idx
}
开始时,i
将包含node
中的所有值。唯一值得购买的股票是价值低于A[0..i - 1]
的股票。我们可以使用A[i]
查询O(lg n)
中这些股票的数量和总和。如果这些股票的总和为count_sum_nodes_upper_bound
,而其数量为s
,则这些股票的总收益就是所有股票的卖出数量(c
)减去每只股票的购买价值。 (A[i] * c
。
此后,可以通过过滤s
(或根据您的需要扩展BST的功能)在O(n)
中轻松地购买股票。
答案 1 :(得分:2)
我们还可以在O(n log n)
时间和O(n)
空间中使用数组,堆栈和二进制搜索来解决此问题。向后迭代,并且在每次迭代时,如果该值大于数组中的最后一个元素,则向数组记录中添加(value, index, 0, 0)
((value, index, count, cost)
);否则,找到第一个较高的元素(使用二进制搜索),增加其数量和成本,并将索引添加到贡献者堆栈的前面。
0 1 2 3 4 5
70 40 90 110 80 100
i:5
[(100, 5, 0)]
contributors: []
i:4
80 < 100
[(100, 5, 1, 80)]
contributors: [4]
i:3
110 > 100
[(100, 5, 1, 80), (110, 3, 0, 0)]
contributors: [4]
i:2
90 < 100
[(100, 5, 2, 170), (110, 3, 0, 0)]
contributors: [2, 4]
i:1
40 < 100
[(100, 5, 3, 210), (110, 3, 0, 0)]
contributors: [1, 2, 4]
i:0
70 < 100
[(100, 5, 4, 280), (110, 3, 0, 0)]
contributors: [0, 1, 2, 4]
现在遍历我们单调增加的新记录。在记录的每次迭代中,添加前一个元组的计数和成本,然后在贡献者堆栈中的每个元素的索引大于记录中的当前元素时,对“贡献者”堆栈中的每个元素进行“弹出”计数和成本:
0 1 2 3 4 5
70 40 90 110 80 100
[(100, 5, 4, 280), (110, 3, 0, 0)]
i:0
best = 4 * 100 - 280 = 120
i:1
add count and cost:
(110, 3, 0 + 4, 0 + 280)
pop count and cost for each
contributor with a greater index:
contributors: [0, 1, 2, 4]
index 4 > 3
(110, 3, 4 - 1, 280 - 80)
profit = 3 * 110 - 200 = 130
best = 130
contributors: [0, 1, 2]