Я работал над версией Leetcode проблемы захваченного дождя (см. ниже). Мое решение проходит почти все тестовые примеры, но я получаю сообщение об ошибке «Превышен лимит времени» в тесте 318, который представляет собой очень длинный ввод. Я новичок, так может ли кто-нибудь сказать мне, какая часть моего решения занимает больше всего времени? Мой код находится после описания проблемы.
Учитывая n неотрицательных целых чисел, представляющих карту высот, где ширина каждой полосы равна 1, вычислите, сколько воды она может удержать после дождя.
Пример 1:
Ввод: высота = [0,1,0,2,1,0,1,3,2,1,2,1]
Выход: 6
Пояснение: Приведенная выше карта высот (черное сечение) представлена массивом [0,1,0,2,1,0,1,3,2,1,2,1]. В данном случае задерживается 6 единиц дождевой воды (синяя секция).
Пример 2:
Ввод: высота = [4,2,0,3,2,5] Выход: 9
Ограничения:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
Код:
class Solution(object):
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
volume = 0
ceiling = 0
for i in height:
if i > ceiling:
ceiling = i
while ceiling > 0:
list_of_peaks = []
for i in range(0,len(height)):
if height[i] >= ceiling:
list_of_peaks.append(i)
for elem in range(0,len(list_of_peaks)):
try:
volume = volume + (list_of_peaks[elem+1]-list_of_peaks[elem]-1)
except:
break
ceiling -= 1
return volume
Я пытался определить, какая часть моего решения неэффективна, но у меня недостаточно опыта, чтобы определить, что необходимо улучшить.
Конечно, вложенные циклы являются проблемой. Посмотрите, сможете ли вы придумать однопроходное решение, в котором вы собираете «воду» после последнего локального максимума, или, возможно, двухпроходное решение, в котором вы определяете впадины, а затем перебираете их.
Это связано с тем, что временная сложность вашего кода равна O (нм), где n — количество элементов по высоте, а m — максимальное значение высоты. В этом случае n*m может достигать не более 2*10^4*10^5. Вам следует рассмотреть другой алгоритм, такой как DP.
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
n = len(height)
leftMax = [height[0]] + [0] * (n - 1)
for i in range(1, n):
leftMax[i] = max(leftMax[i - 1], height[i])
rightMax = [0] * (n - 1) + [height[n - 1]]
for i in range(n - 2, -1, -1):
rightMax[i] = max(rightMax[i + 1], height[i])
ans = sum(min(leftMax[i], rightMax[i]) - height[i] for i in range(n))
return ans
Давайте разобьем ваш алгоритм по временной сложности.
for i in height:
if i > ceiling:
ceiling = i
Вы пытаетесь найти самую высокую высоту. Это O(n)
временная сложность. Кстати, в Python можно просто сделать ceiling = max(height)
.
while ceiling > 0:
list_of_peaks = []
for i in range(0,len(height)):
if height[i] >= ceiling:
list_of_peaks.append(i)
for elem in range(0,len(list_of_peaks)):
try:
volume = volume + (list_of_peaks[elem+1]-list_of_peaks[elem]-1)
except:
break
ceiling -= 1
Здесь ваш цикл while выполняется ceiling
количество раз (потому что вы каждый раз уменьшаете ceiling
на 1). На каждой итерации цикла while вы пытаетесь найти все столбцы, высота которых как минимум равна текущему рассматриваемому потолку. Таким образом, временная сложность доходит до O(ceiling * n)
. Здесь ваш алгоритм становится очень медленным.
Подумайте о таком вводе, когда у вас есть одна очень высокая полоса.
x
x
x
x
x
x x
xx x
xx xx
x xxxxxxx
Ваш цикл while выполняется столько раз, сколько самая высокая полоса, что совершенно не нужно.
Вместо этого проблему можно решить таким образом за O(n)
время.
class Solution:
def trap(self, heights: list[int]) -> int:
if not heights or len(heights) <= 2:
return 0
n = len(heights)
# left_max[i] gives you the index of the tallest bar to the left of index i
left_max = [0] * n
# right_max[i] gives you the index of the tallest bar to the right of index i
right_max = [0] * n
# Fill left_max array
left_max[0] = heights[0]
for i in range(1, n):
left_max[i] = max(left_max[i - 1], heights[i])
# Fill right_max array
right_max[n - 1] = heights[n - 1]
for i in range(n - 2, -1, -1):
right_max[i] = max(right_max[i + 1], heights[i])
# Calculate the trapped water at each column
volume = 0
for i in range(n):
volume += max(min(left_max[i], right_max[i]) - heights[i], 0)
# Let us analyse the above line:
#
# min(left_max[i], right_max[i]) because, at each index,
# the volume of water trapped is limited by
# the shorter of the tallest bars on the left and right.
#
# - heights[i] because this column of water will stand
# on the current bar.
#
# max(..., 0) because if there is no bar taller than the
# current bar on the left and right, then it would be 0.
return volume
зачем пытаться, кроме как внутри цикла?