Что такое стек

Что такое стек

Стек — это вид структуры данных, в котором элементы упорядочены и добавление или удаление элементов происходит с верхней части стека — по принципу «Последним пришел — первым ушел» (LIFO). Реализовать стек можно, например, С, JavaScript, С++, Java, Python, Lisp или C#.

Как работает стек

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

Стек придерживается линейной связи, в которой данные располагаются в строгом порядке и нарушить данную последовательность невозможно. Данные, оказавшиеся в стеке последними, должны использоваться первыми.

Четыре основные операции над стеком:

  • push — добавление элемента
  • pop — удаление верхнего элемента
  • isEmpty — проверка стека на наличие в нем элементов
  • peek — возвращение последнего элемента, не удаляя его.

Разновидности стеков

Существует несколько видов стека:

  • Стек вызовов: область памяти, в которой хранится информация о точках перехода между элементами программного кода. Например: скрипт вызывает функцию, интерпретатор записывает ее в стек вызовов. Любые функции, вызванные этой функцией, добавляются в стек и выполняются по очереди. Когда основная функция отработает, интерпретатор извлечет ее из стека вызовов и продолжит выполнять код с того места, где он остановился ранее.
  • Стек на основе массива: в этом типе стека элементы хранятся в массиве фиксированного размера, и доступ к ним осуществляется по индексу. Главным недостатком является ограниченность размера массива, что может привести к проблемам с добавлением новых элементов, если стек заполнен. Также этот тип стека может быть неэффективным, если требуется частое добавление и удаление элементов.
  • Стек на основе связного списка: в нем элементы хранятся в связном списке, где каждый элемент содержит указатель на следующий элемент. Этот тип стека позволяет эффективно добавлять и удалять элементы, но может быть менее эффективным при доступе к элементам по индексу. Одним из преимуществ стека на основе связного списка является его динамичность — размер стека может быть изменен в любой момент времени.
  • Стек на основе двухсторонней очереди (Deque): в этом случае элементы хранятся в двухсторонней очереди, где элементы могут быть добавлены или удалены с обоих концов.
  • Стек на основе дерева: здесь элементы хранятся в дереве, где каждый узел содержит указатель на родительский узел. Этот тип стека может быть полезен для решения задач, связанных с деревьями, таких как обход дерева в глубину.
  • Стек на основе кучи (Heap): в этом виде стека элементы хранятся в куче, где каждый элемент имеет приоритет. Этот тип стека может быть полезен для решения задач, связанных с приоритетами, таких как поиск минимального или максимального элемента.

Переполнение стека

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

Причины переполнения стека

Рекурсия: если функция вызывает саму себя слишком много раз, то каждый новый вызов функции добавляет новый элемент в стек. Если глубина рекурсии слишком большая, то стек может переполниться.

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

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

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

Опасности переполнения стека

  • Падение программы: переполнение стека может привести к аварийному завершению программы, что может привести к потере данных или другим негативным последствиям.
  • Потеря данных: если стек переполнен, то данные, которые не помещаются в стек, могут быть потеряны.
  • Уязвимость безопасности: переполнение стека может привести к уязвимостям безопасности, так как злоумышленник может использовать переполнение стека для выполнения вредоносного кода или других опасных действий.
  • Непредсказуемое поведение программы: переполнение стека иногда запускает непредсказуемое поведение программы, включая зависание программы или ошибки в работе.
Важно следить за использованием стека и предотвращать его переполнение, например, путем использования итерационных алгоритмов вместо рекурсии и проверки глубины стека.

Реализация стека

Стек может быть выполнен с помощью связанного списка, массива фиксированной длины или динамического массива.

Пример стека на массиве в JavaScript

const arr = [];
//Добавляем элементы в массив
arr.push("Один");
console.log(arr); // выведет ["Один"]

arr.push('Два');
console.log(); // выведет ["Один", "Два"]

//Удаляем элемент
arr.pop();
console.log(arr); // выведет ["Один"]

//Добавим еще один элемент
arr.push("Сосиска");

//Отобразим последний элемент, не удаляя его
console.log(arr.peek()); // выведет Сосиска

//Проверим, пуст ли наш стек
console.log(arr.isEmpty()); // выведет false

Пример стека на связанном списке в Python

class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None


class LinkedStack:
    def __init__(self):
        self.top = None

    # Проверим, пуст ли наш стек
    def is_empty(self):
        return self.top is None

    # Добавляем элемент в стек
    def push(self, item):
        new_node = Node(item)
        new_node.next = self.top
        self.top = new_node

    # Удаляем элемент из вершины стека и возвращаем его
    def pop(self):
        if self.is_empty():
            raise Exception("Stack is empty")
        item = self.top.data
        self.top = self.top.next
        return item

    # Отобразим последний элемент, не удаляя его
    def peek(self):
        if self.is_empty():
            raise Exception("Stack is empty")
        return self.top.data

Класс Node представляет узел связанного списка, а класс LinkedStack использует узлы, чтобы создать стек. Каждый узел содержит данные (data) и ссылку на следующий узел (next). При вызове метода push, создается новый узел с переданным элементом, и ссылка на этот узел устанавливается в вершину стека (top). При вызове метода pop, элемент извлекается из вершины стека, и вершина обновляется, чтобы указывать на следующий узел в списке.

Софья Пирогова

Софья Пирогова

Главный редактор / Автор статей