Skip to main content

Стек

Описание

Стек (stack) - это динамическая структура данных, в которой элементы хранятся в порядке, обратному порядку их добавления. Она, как и очередь, является примером линейной структуры или последовательной коллекции. Новые элементы добавляются в начало стека (push). Удаление элементов также выполняется с начала стека (pop). Таким образом, стек реализует принцип "последним вошел - первым вышел" (last in - first out, LIFO). Кроме push и pop, часто также реализуется операция чтения значения головного узла без его удаления (peek).

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


Реализация

Для реализации стека мы также воспользуемся связным списком (см. раздел 1).

Привожу сразу весь код:

// data-structures/stack.js
// Импортируем конструктор связного списка
import LinkedList from './linked-list'

// Стек
export default class Stack {
constructor() {
// Создаем связный список
this.list = new LinkedList()
}

// Проверяет, является ли стек пустым
isEmpty() {
return !this.list.head
}

// Возвращает значение первого узла без его удаления
peek() {
if (this.isEmpty()) {
return null
}
return this.list.head.value
}

// Добавляет элемент в начало стека
push(value) {
this.list.prepend(value)
}

// Удаляет первый узел и возвращает его значение
pop() {
const removedHead = this.list.removeHead()
return removedHead?.value || null
}

// Преобразует стек в строку
toString(cb) {
return this.list.toString(cb)
}

// Преобразует стек в массив значений
toArray() {
return this.list.toArray().map((node) => node.value)
}
}

Тестирование

Проверяем, что наш стек работает, как ожидается:

// data-structures/__tests__/stack.test.js
import Stack from '../stack'

describe('Stack', () => {
it('должен создать пустой стек', () => {
const stack = new Stack()
expect(stack).not.toBeNull()
expect(stack.linkedList).not.toBeNull()
})

it('должен добавить значения в стек', () => {
const stack = new Stack()

stack.push(1)
stack.push(2)

expect(stack.toString()).toBe('2,1')
})

it('должен проверить пустоту стека', () => {
const stack = new Stack()

expect(stack.isEmpty()).toBe(true)

stack.push(1)

expect(stack.isEmpty()).toBe(false)
})

it('должен извлечь значения из стека без удаления узлов', () => {
const stack = new Stack()

expect(stack.peek()).toBeNull()

stack.push(1)
stack.push(2)

expect(stack.peek()).toBe(2)
expect(stack.peek()).toBe(2)
})

it('должен извлечь значения из стека с удалением узлов', () => {
const stack = new Stack()

stack.push(1)
stack.push(2)

expect(stack.pop()).toBe(2)
expect(stack.pop()).toBe(1)
expect(stack.pop()).toBeNull()
expect(stack.isEmpty()).toBe(true)
})

it('должен добавить/удалить объекты в/из стека', () => {
const stack = new Stack()

stack.push({ value: 'test1', key: 'key1' })
stack.push({ value: 'test2', key: 'key2' })

const stringifier = (value) => `${value.key}:${value.value}`

expect(stack.toString(stringifier)).toBe('key2:test2,key1:test1')
expect(stack.pop().value).toBe('test2')
expect(stack.pop().value).toBe('test1')
})

it('должен преобразовать стек в массив', () => {
const stack = new Stack()

expect(stack.peek()).toBeNull()

stack.push(1)
stack.push(2)
stack.push(3)

expect(stack.toArray()).toEqual([3, 2, 1])
})
})

Запускаем тесты:

npm run test ./data-structures/__tests__/stack