这是在高盛的采访中向我询问的.
我们有一个数组,其中第i个元素表示保持在第i个位置的块数(固定高度和宽度),如在水捕获问题中.我们必须从顶部移除最少数量的块,以便在下雨时没有水被困住.
我想出的解决方案是我们必须确保最终结构是这样的,即某些xth是峰值,并且左右的元素保持不增加直到达到极限.我们可以为每个元素执行此操作,并找到所有结果的最小值.
我做的一个轻微的优化是我可以检查任何第i个位置是否可以达到峰值.如果左边和右边至少有一个元素的高度低于当前位置,那么任何第i个元素都可以是峰值.
面试官让我以某种方式使用水陷阱问题中使用的左右阵列(参考号:https://www.geeksforgeeks.org/trapping-rain-water/),这是我无法做到的.
我想知道是否有更好的解决方案来解决上述问题.
最佳答案 我们需要回答这个问题:
>哪一列会产生最佳峰值,即我们需要移除最小数量的块以使其向两侧减少(不增加)?
要回答这个问题,对于每一栏,我们都需要回答以下问题:
>需要删除多少块以使其成为峰值?
我们可以将其分为两个问题:
> L(i)=需要向左移除多少块?
> R(i)=需要向右移除多少个块?
L(i)可以推导如下:
>如果左边的列小于或等于,那么使峰值的块数与使其成为峰值的块数相同.从而:
if(列i-1< =列i)L(i)= L(i-1)
>如果左边的列更大,我们需要找到左边的第一列j小于或等于i.然后要移除(向左)以使ia达到峰值(L(i))的块将是要移除的块以使ja峰值(L(j))加上要移除的块以使一切介于两者之间对我
如果我们可以循环遍历列到达那里,这将是足够简单的,但要有效地执行它需要更复杂的东西.
请注意,我们只关心第一个小于i的元素j,这意味着该元素j左侧的任何元素k都大于j,我们根本不关心(因为任何可能返回k的元素, k <= i,在它能够做到之前将返回j,因为j <= k,因此j <= i).这也意味着我们只需要处理任何给定的列一次,因为如果我们通过列来查找更少的列,那么我们所看到的一切都会更大,因此与右边的列无关. 这引出了以下内容:
>保持一堆不断增加的列.
技术说明:我们需要在列中存储列的索引,而不是列的值,因为我们还需要知道列的位置.对于下面的示例,我只是将值用于可读性.
>使用此堆栈,我们可以通过从堆栈中弹出大于列i的元素并将弹出值与列i之间的差异和距离相乘来快速计算任何给定的L(i).
这是一个让它不那么抽象的例子.如果我们有:
[2,6,4,9,5,3]
0,1,2,3,4,5 i
0,0,2,2,6,12 L(i)
stack after 5 = [2,4,5]
stack after 3 = [2,3]
[2,4,4,5,5,3] to make 5 a peak
[2,3,3,3,3,3] to make 3 a peak
为了使5峰值(L(4)= 6),我们将[2,4,4,5,5,3] – 也就是说,5和下一个较小元素4之间的所有元素应该等于5,并且4和下一个较小元素2之间的所有元素应该等于4.
要从这里将3变为峰值,我们需要将上面的(6)添加到块中以移除以将所有5变为3(即2列*(5-3)= 4)和4变为3(其中是2列*(4-3)= 2).
例如,如果我们在此之后获得1,我们可以仅使用2和3来做同样的事情(因为我们已经知道使中间的所有列等于3的成本).
R(i)可以以与L(i)完全相同的方式导出,只是方向相反.
最后,我们只需返回将产生最佳峰值的列,即min(L(i)R(i)).
这需要O(n)时间和O(n)空间.
这是一些Python 3代码,它执行此操作:
# x[-1] is last element of x
# We add an element to the start to simplify the code
def calc_L_R(a, L):
a.insert(0, 0)
stack = [0]
for i in range(1, len(a)):
if a[i-1] <= a[i]:
L.append(L[-1])
stack.append(i)
else:
rem = L[-1]
while a[i] < a[stack[-1]]:
j = stack.pop()
rem += (j - stack[-1]) * (a[j] - a[i])
L.append(rem)
stack.append(i)
a.remove(0)
def no_water_trapped(a):
L = [0]
calc_L_R(a, L)
a.reverse()
R = [0]
calc_L_R(a, R)
R.reverse()
return min(L[i] + R[i] for i in range(len(L)))
print(no_water_trapped([4,1,4,1,4])) # 6
print(no_water_trapped([6,4,5,3,3,8,7,9])) # 7
print(no_water_trapped([3,3,4,3,3])) # 0