Освоение структур данных в Ruby — Стеки

Стеки — это особый вид связанных списков, которые позволяют нам эффективно хранить извлекаемые данные в порядке «последним поступил — первым вышел» (LIFO). Или, другими словами, элементы извлекаются в порядке, обратном их хранению в стеке.

Так же, как это происходит с хвостыстеки не позволяют нам вставлять/удалять элементы в случайных местах, и, как и очереди, они представляют собой не более чем последовательности соединенных узлов.

Виртуальные машины на основе стека, такие как YARV (виртуальная машина Ruby), используют стеки для хранения и оценки инструкций байт-кода.

Давайте посмотрим, как растет стек, когда мы сохраняем (вталкиваем) в него элементы.

push(1) push(2) push(3) 
--- --- ---
 1   2   3 
--- --- --- 
 1   2 
--- --- 
 1 
---

Теперь давайте посмотрим, как он сжимается, когда мы удаляем (извлекаем) из него элементы.

pop pop pop
--- --- --- 
 3   2   1
--- --- --- 
 2   1
--- --- 
 1 
---

Интерфейс стеков

Интерфейс для стеков похож на хвосты в том смысле, что у них не так много методов, что приятно, потому что интерфейс прост в освоении и его трудно не использовать.

Смысл вспомогательных методов для этой структуры данных тот же, что и для очередей — их нет —


Интерфейс стека. Имена методов, резюме и сложность.

Детали реализации

Элементы в стеке представлены узлами, которые содержат два атрибута:

  • Значение, которое содержит текущий узел.
  • Указатель на следующий узел в стеке.

Первый узел стека называется головным, а последний — хвостовым. Чтобы отметить конец стека, мы должны установить следующий указатель последнего элемента в ноль.

В отличие от того, что происходит с обычными связанными списками, при работе со стеками вы не должны использовать начало и конец непосредственно из своего кода. Самый безопасный способ взаимодействия со стеками — через нажать, заглянуть и поп методы.

Далее следует краткое описание каждого метода, его сложности и исходного кода.

Инициализировать

Этот метод является конструктором стека. Он создает пустой стек, устанавливает головной узел в nil и длину стека в 0.

Сложность этого метода составляет O(1).

def initialize
  self.head = nil
  self.length = 0
end

Толкать

Создает новый узел для вставки значения в стек. Новый узел перемещает элемент, стоящий во главе списка, и становится новым главой списка.

Поскольку стек хранит указатели на его голову и хвост, сложность этого метода составляет O(1).

def push data
  node = Node.new data
  if length == 0
    self.tail = node
  end

  node.next = self.head
  self.head = node
  self.length += 1
end

Поп

Удаляет элемент из стека. Как и в случае с очередями, удаление выполняется просто, потому что оно всегда происходит в начале стека.

Чтобы удалить элемент из стека, мы указываем голову на узел, который находится рядом с ним, и присваиваем хвосту значение nil, если стек содержит только один элемент.

Сложность этого метода составляет O(1).

def pop
  return nil unless self.length > 0
  self.head = self.head.next
  self.tail = nil if self.length == 1
  self.length -= 1
end

заглянуть

Возвращает узел, находящийся во главе стека, не удаляя его, или nil, если стек пуст.

Поскольку pop не возвращает удаленный элемент, peek — это путь, если вам нужно что-то сделать с этим элементом.

Сложность этого метода составляет O(1).

def peek
  self.head
end

чистый

Извлекает все элементы из стека.

Сложность этого метода составляет O(n).

def clear
  while self.peek
    pop
  end
end

Каждый

Этот метод просматривает стек, возвращая по одному, пока не достигнет последнего элемента.

Сложность получения следующего элемента в стеке составляет O (1). Сложность получения всего стека составляет O(n).

def each
  return nil unless block_given?
  current = self.head
  while current
    yield current
    current = current.next
  end
end

Распечатать

Распечатайте содержимое стека, пройдя его элементы.

Сложность этого метода составляет O(n).

def print
  if self.length == 0
    puts "empty"
  else
    self.each { |node| puts node.data }
  end
end

Когда использовать стеки

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

Стеки отлично подходят для управления последовательностями инструкций, анализа выражений и определения приоритета операторов; назвать несколько.

Так что это в значительной степени о стеках. Я надеюсь, что вам понравилось!

В следующий раз мы рассмотрим Хэш-таблицы.

Спасибо за чтение!

PS: если вы являетесь участником программы Kindle Unlimited, вы можете прочитать немного отполированную версию этой серии бесплатно на вашем устройстве Kindle.


Структуры данных с нуля — Ruby Edition.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *