Алгоритмы прекрасны тем, что они могут решать сложные проблемы удивительно простыми способами. Однако полезность алгоритма проявляется и тогда, когда он предлагает более эффективный (или интересный) способ решения достаточно простой задачи.
Предположим, мы хотим найти наибольшее число в стеке, полностью состоящем из целых чисел. Как бы мы это сделали?
Мы должны помнить, что максимальное значение в стеке потенциально может меняться каждый раз, когда мы выталкиваем и выталкиваем из стека. Это особенно заметно в отсортированном стеке с различными значениями, где максимальное значение всегдаизменяется.
Мы не хотим переопределять максимум каждый раз, когда мы помещаем и выталкиваем в стек, если мы можем помочь. И если мы это сделаем, мы не хотим, чтобы процесс поиска был дорогостоящим.
Наивное решение
Ваш первый инстинкт поиска максимального значения в стеке, вероятно, отражен в приведенном ниже коде Ruby, который создает простой класс Stack с добавленным методом «max».
class Stack def initialize @stack = [] @max = nil end def push(val) @stack.push(val) @max = max end def pop @stack.pop @max = max end def peek(index) raise ArgumentError unless ([email protected]).include?(index) @stack[index] end def max return nil if @stack.empty? max = peek(0) @stack.length.times do |i| max = @stack[i] if max < @stack[i] end max end end
Поиск максимума таким образом работает, но не слишком эффективно. Временная сложность метода «max» — это линейное время, или O(n), поскольку нам приходится просматривать весьстек каждый раз, когда мы хотим найти максимальное значение. Мы также запускаем этот алгоритм O(n) каждый раз, когда мы нажимаем и выталкиваем! Там должен быть лучший способ.
Мы должны стремиться к постоянному времени, или O(1), так как это золотой стандарт для поиска в целом. Но как нам достичь этой цели?
Умное решение
К счастью, есть алгоритм, который помогает нам быстро находить и отслеживать максимум стека по мере его создания. Этот алгоритм показан в коде Ruby ниже:
class Stack def initialize @stack = [] @max_stack = [] # a second stack! end def push(val) @stack.push(val) if @max_stack.empty? || val > peek_max @max_stack.push(val) end end def pop last = @stack.pop @max_stack.pop if last == peek_max end def peek(index) raise ArgumentError unless ([email protected]).include?(index) @stack[index] end def peek_max @max_stack[-1] end def inspect @stack end def max peek_max end end
Этот алгоритм на самом деле основан на использовании двухстеков: stack
и max_stack
.stack
отслеживают целые числа, а max_stack
отслеживает максимальные значения при построении стека.
Когда мы нажимаем на stack
и обнаруживаем, что переданное значение больше, чем значение, расположенное вверху max_stack
, мы также нажимаем это значение на max_stack
.
Когда мы извлекаем из stack
и обнаруживаем, что извлеченное значение совпадает со значением, расположенным вверху max_stack
, мы также извлекаем последнее значение из max_stack
. Значение должно быть добавлено в оба стека вместе, поэтому они должны быть удалены вместе.
Наконец, получить максимальное значение стека — это просто посмотреть на самое верхнее значение max_stack
с помощью метода peek_max
. Нам удалось найти максимум за время O(1) простым поиском!
Вывод
Как и все алгоритмы, этот алгоритм «двойного стека» не идеален. Предполагается, что стек изначально пуст, что позволяет нам отслеживать максимальное значение по мере создания стека. Если стек уже инициализирован значениями и мы еще не отслеживаем максимальное значение, мы должны сделать одно из следующих действий:
- Перестройте стек, создав стек максимальных значений, поскольку обычный стек перестраивается.
- Используйте метод max в «наивном» решении или другой метод, не основанный на построении стека.
Кроме того, при использовании алгоритма двойного стека возрастает сложность пространства, поскольку нам нужен совершенно новый стек для отслеживания максимальных значений. Вам решать, стоит ли функциональность O(1) push, pop и max lookup дополнительного необходимого пространства.
Тем не менее, как и все алгоритмы, этот, безусловно, полезно держать в наборе инструментов вашего программиста.
Предоставлено Interview Cake.