Почему мое решение Python для проблемы захваченного дождя работает так медленно?

Я работал над версией Leetcode проблемы захваченного дождя (см. ниже). Мое решение проходит почти все тестовые примеры, но я получаю сообщение об ошибке «Превышен лимит времени» в тесте 318, который представляет собой очень длинный ввод. Я новичок, так может ли кто-нибудь сказать мне, какая часть моего решения занимает больше всего времени? Мой код находится после описания проблемы.

Учитывая n неотрицательных целых чисел, представляющих карту высот, где ширина каждой полосы равна 1, вычислите, сколько воды она может удержать после дождя.

Пример 1:

Почему мое решение Python для проблемы захваченного дождя работает так медленно?

Ввод: высота = [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

Я пытался определить, какая часть моего решения неэффективна, но у меня недостаточно опыта, чтобы определить, что необходимо улучшить.

зачем пытаться, кроме как внутри цикла?

Timsib Adnap 20.07.2024 11:10

Конечно, вложенные циклы являются проблемой. Посмотрите, сможете ли вы придумать однопроходное решение, в котором вы собираете «воду» после последнего локального максимума, или, возможно, двухпроходное решение, в котором вы определяете впадины, а затем перебираете их.

tripleee 20.07.2024 11:18
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
73
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Это связано с тем, что временная сложность вашего кода равна 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

Другие вопросы по теме