我在接受采访时被问到这个问题:
Best Time to Buy and Sell Stock
假设您有一个数组,其中第i个元素是第i天给定股票的价格。 如果您只被允许购买一股股票并卖出一股股票,那么设计一种算法来寻找买卖的最佳时机。我能够给出O(n)算法。
虽然面试官问我一个问题,但是“买一个,卖一个”,然后“买一个,卖一个”是什么情况,这意味着一天有两笔交易,最大化利润。我能够给出一个O(n ^ 2)算法。但采访者表示可以改进。有O(n)算法吗?
面试官说你不能同时买两份。你必须买一个卖,然后在另一个时间买一个,然后卖掉它。答案 0 :(得分:1)
原始问题reference中给出的O(n)解决方案为原始数组的每个前缀提供了最佳的“买一个然后卖出一个”答案。该算法也可以通过简单的修改来应对“向后”的情况;即从阵列向后工作的“卖一个然后买一个”;这相当于为阵列的每个后缀添加“买一送一”的答案。
现在,在“买入,卖出,买入,卖出”的情况下,我们有一点(在第一次卖出之后)我们在阵列中的某个位置,比如 b 。对于该断点,最佳解决方案是 0..b 的最佳前缀解决方案和 b + 1..n 的最佳后缀解决方案。最好的“买入,卖出,买入,卖出”是这些最佳解决方案的最佳选择。
因此,要解决 O(n)中的“买,卖,买,卖”,您可以在 O(n)中解释前缀,后缀为 O(n),然后为每个断点计算最佳值 - 所以 nO(1)。这是使用 O(n)空间的 O(n)算法。
答案 1 :(得分:0)
可以优化它只有一个for循环,但想法基本相同:
int maxProfit(vector<int> &prices) {
if (prices.empty()) return 0;
int n = prices.size();
int lHolding = prices[0], rHolding = prices[n-1];
int lMax = 0, rMax = 0;
vector<int> profit(n);
for (int i = 1; i < n; i++) {
if (prices[i] > lHolding) {
lMax = max(lMax, prices[i] - lHolding);
} else {
lHolding = prices[i];
}
profit[i] += lMax;
int right = n - 1 - i;
if (rHolding > prices[right]) {
rMax = max(rMax, rHolding - prices[right]);
} else {
rHolding = prices[right];
}
profit[right] += rMax;
}
return *max_element(profit.begin(), profit.end());
}
答案 2 :(得分:0)
我迟到了比赛,但现在是。首先,由可怕的解决方案提供的解决方案并不十分清楚,因为有可能有人实现并得到O(n ^ 2)。这只是通过循环遍历b,对于每个b,计算前缀和后缀,每个都是O(n),所以它一起是O(n)*(n)其中(n)来自b = 1 ... n 。这意味着结果是O(n ^ 2)。
要实现该描述以使其在O(n)中运行,需要认识到前缀和后缀是独立的,并且可以由Daniel实现单独运行。
此解决方案的问题是它需要O(n)空间。假设您的库存跟踪是1千兆点(例如,如果您要重复使用2年的数据,则非常常见),那么您将耗尽内存使用量。
如果我是面试官,下一个合乎逻辑的问题是:是否可以删除内存要求?
让我试一试。其中大部分只是基础数学。
我们有一个数组价格[0 ... n-1]。
在原始问题中,我们希望找到r,s其中0&lt; = r&lt; s&lt; n这样
p =价格[s] - 价格[r]
最大化。
在新问题中,我们希望找到i,j,k,l其中0&lt; = i&lt; j&lt; k&lt; l&lt; n&lt; n
q =价格[j] -prices [i] +价格[l] -prices [k]
最大化。
(假设n> = 4,即没有退化情况,我们可以轻松处理)
假设我们解决了原问题,那么新问题的解决方案可分为3种情况:
通过减少荒谬来证明这一点很容易(即假设没有,然后扰乱i或k到r和j或l到s。这种扰动会增加q,与原始假设相矛盾。)
好的,解决方案很简单:
一个。在O(n)中找到r,s 湾检查O(n)中的案例1 C。在O(n)中检查案例2 d。在O(n)中检查案例3 即返回b,c,d的最大值。
实际上,b,c和d可以在n次迭代中执行。总共为2 O(n)+ O(1),其为O(n)。还有一些固定数量的变量,因此空间要求为O(1)。
int maxProfit(vector<int> &prices) {
int n = prices.size();
int b, a;
int m1 = 0;
int hb;
b = a = 0;
hb = 0;
for (int i = 1; i < n; i++) {
if (prices[i] < prices[hb]) {
hb = i;
}
if (prices[i] - prices[hb] > m1) {
m1 = prices[i] - prices[hb];
b = hb; a = i;
}
}
int before = 0, after = 0;
if (b > 0) {
int bb, ba;
before = 0;
int bhb;
bb = ba = 0;
bhb = 0;
for (int i = 0; i < b-1; i++) {
if (prices[i] < prices[bhb]) {
bhb = i;
}
if (prices[i] - prices[bhb] > before) {
before = prices[i] - prices[bhb];
bb = bhb; ba = i;
}
}
}
if (a < n) {
int ab, aa;
int ahb;
ab = aa = a+1;
ahb = a+1;
after = 0;
for (int i = a+2; i < n; i++) {
if (prices[i] < prices[ahb]) {
ahb = i;
}
if (prices[i] - prices[ahb] > after) {
after = prices[i] - prices[ahb];
ab = ahb; aa = i;
}
}
}
int hmb = before + m1;
int hma = after + m1;
int hm = max(hmb,hma);
int tm = b;
for (int j = b+1; j < a; j++) {
if (prices[j] > prices[tm]) {
tm = j;
}
m1 = prices[tm] - prices[b] + prices[a] - prices[j];
hm = max(hm,m1);
}
return hm;
}
我可能会忘记一些堕落的案例,但是写这个答案要比提出简单的解决方案花费更长的时间,所以如果有错误,你就是靠自己。数学似乎很有用,所以它可能是正确的。