← Назад к вопросам

Какая сложность у разворота односвязного списка?

2.0 Middle🔥 162 комментариев
#Алгоритмы и структуры данных

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сложность разворота односвязного списка

Алгоритмическая сложность разворота односвязного списка составляет O(n) по времени и O(1) по дополнительной памяти (если реализован итеративно), где n — количество элементов в списке. Это оптимальная сложность для данной операции, так как необходимо обработать каждый узел ровно один раз.

Детальный анализ сложности

Временная сложность O(n)

  • Алгоритм проходит по каждому узлу списка ровно один раз
  • Для каждого узла выполняется фиксированное количество операций:
    • Переопределение указателей (обычно 3-4 операции присваивания)
    • Перемещение указателей на следующие узлы
  • Независимо от реализации (итеративная или рекурсивная), количество операций пропорционально количеству узлов
// Пример итеративной реализации на PHP
class ListNode {
    public $val;
    public $next;
    
    public function __construct($val = 0, $next = null) {
        $this->val = $val;
        $this->next = $next;
    }
}

function reverseLinkedList($head) {
    $prev = null;
    $current = $head;
    
    // Проходим по всем n узлам
    while ($current !== null) {
        $next = $current->next;  // Сохраняем следующий узел
        $current->next = $prev;   // Разворачиваем указатель
        $prev = $current;         // Перемещаем prev
        $current = $next;         // Перемещаем current
    }
    
    return $prev; // Новая голова списка
}

Пространственная сложность

Итеративная реализация: O(1)

  • Используется фиксированное количество дополнительных переменных (указателей)
  • Не зависит от размера списка
  • Только указатели prev, current, next занимают постоянную память

Рекурсивная реализация: O(n)

function reverseLinkedListRecursive($head) {
    // Базовый случай
    if ($head === null || $head->next === null) {
        return $head;
    }
    
    // Рекурсивный вызов для n узлов
    $reversed = reverseLinkedListRecursive($head->next);
    
    // Разворот указателей
    $head->next->next = $head;
    $head->next = null;
    
    return $reversed;
}
  • Каждый рекурсивный вызов добавляет фрейм в стек вызовов
  • Глубина рекурсии равна n (количеству узлов)
  • Пространственная сложность O(n) из-за использования стека вызовов

Почему нельзя сделать быстрее O(n)?

  1. Необходимость обработки каждого узла: Для корректного разворота нужно изменить указатель next у каждого элемента
  2. Однонаправленность структуры: В односвязном списке есть только указатель на следующий элемент, нет доступа к предыдущему без последовательного прохода
  3. Минимально необходимые операции: Даже если бы у нас была дополнительная информация о списке, физическое изменение n указателей требует O(n) операций

Практические аспекты и оптимизации

Особенности реализации на PHP

// Оптимизированная версия с проверкой краевых случаев
function reverseListOptimized($head) {
    if ($head === null || $head->next === null) {
        return $head;
    }
    
    $prev = null;
    $current = $head;
    
    while ($current !== null) {
        // Меньше временных переменных
        [$current->next, $prev, $current] = [$prev, $current, $current->next];
    }
    
    return $prev;
}

Сравнение подходов

КритерийИтеративныйРекурсивный
ВремяO(n)O(n)
ПамятьO(1)O(n) (стек вызовов)
ЧитаемостьПроще для пониманияЭлегантнее, но сложнее
ОграниченияНетРиск переполнения стека

Вывод

Разворот односвязного списка имеет линейную временную сложность O(n), что является оптимальным для этой операции. Пространственная сложность зависит от реализации: O(1) для итеративного подхода и O(n) для рекурсивного. В production-коде на PHP обычно предпочитают итеративную реализацию из-за постоянной памяти и отсутствия риска переполнения стека при больших списках.