---
title: "Разворот связного списка"
url: https://algopath.pro/ru/patterns/linked-list-reversal
language: ru
summary: "Пройдите список один раз и в каждом узле разверните ссылку на предыдущий узел. Трёх локальных переменных для этого хватит."
updated: 2026-08-24
---

# Разворот связного списка

Пройдите список один раз и в каждом узле разверните ссылку на предыдущий узел. Трёх локальных переменных для этого хватит.

## Разворот связного списка: как это работает?

Держите три ссылки: предыдущий узел, текущий и следующий за текущим. Предыдущий начинается с null.

Сохраните current.next до того, как что-то менять. Иначе остаток списка потеряется.

Направьте current.next на предыдущий узел. Эта одна строка и есть разворот.

Сдвиньте предыдущий на текущий, а текущий на сохранённый узел. Окно продвинулось на один шаг.

Остановитесь, когда текущий стал null. Предыдущий теперь указывает на бывший последний узел.

Верните предыдущий, а не текущий. Бывшая голова стала хвостом.

- `prev = null, cur = 1` Список читается как 1, 2, 3. Пока ничего не развёрнуто.
- `1 указывает на null, cur = 2` Узел 1 теперь смотрит в null. Он стал хвостом.
- `2 указывает на 1, cur = 3` Узел 2 смотрит назад на узел 1. Два узла готовы.
- `3 указывает на 2, cur = null` Последний узел развернулся. Текущий ушёл за конец.
- `голова = 3` В prev лежит новая голова. Три разворота за один проход.

## Разворот связного списка: когда применять?

- развернуть связный список (весь список или между позициями)
- развернуть группами по k
- без дополнительного массива или с O(1) доп. памяти
- поменять местами пары узлов
- односвязный список, доступен только .next

## Разворот связного списка: с чем путают?

- **Слияние и перестройка связного списка** - Слияние сплетает два списка в один. Разворот перевязывает один список на месте.
- **Быстрый и медленный указатели** - Та пара находит середину или цикл обходом. Она не меняет ни одной ссылки.
- **Стек (LIFO)** - Сложить все узлы в стек и снять их тоже даёт разворот. Это стоит O(n) памяти.
- **Рекурсия** - Рекурсивный вариант читается хорошо, но тратит n кадров стека. Цикл обходится тремя переменными.

## Разворот связного списка: сложность по времени и памяти

n до 1e6 даёт O(n) времени и O(1) памяти. Рекурсивная форма стоит O(n) стека.

## Разворот связного списка: разбор примера

### Развернуть только часть списка

Разверните узлы с позиции left по позицию right. Всё вне этого диапазона сохраняет порядок.

Ожидается один проход, копировать значения нельзя.

Дойдите до узла перед left и удержите его. Назовём этот узел якорем.

Потом перетаскивайте каждый следующий узел в начало развёрнутой части. Якорь держит список связным.

```javascript
function reverseBetween(head, left, right) {
    const dummy = new ListNode(0, head);

    let anchor = dummy;
    for (let i = 1; i < left; i++) anchor = anchor.next;

    const tail = anchor.next; // this node ends up last in the reversed part

    for (let i = 0; i < right - left; i++) {
        const moved = tail.next;
        tail.next = moved.next;
        moved.next = anchor.next;
        anchor.next = moved;
    }

    return dummy.next;
}
```

## Разворот связного списка: частые ошибки

- **Теряют остаток списка** Перезапись current.next без сохранения выбрасывает все последующие узлы. Сохраняйте ссылку заранее.
- **Возвращают не тот узел** После цикла текущий равен null. Новая голова это предыдущий.
- **Оставляют старую голову висеть** Она обязана в итоге смотреть в null. Старт предыдущего с null даёт это бесплатно.
- **Берут рекурсию на длинном списке** Миллион узлов означает миллион кадров стека. Это переполняет стек вызовов.

## Разворот связного списка: задачи с собеседований

- **Разворот связного списка** Чистая форма на трёх переменных.
- **Разворот связного списка II** Разворачивается только участок, поэтому нужен якорь.
- **Разворот узлов группами по k** Разверните каждый блок из k, потом соедините блоки.
- **Палиндром в связном списке** Разверните вторую половину и сравните с первой.
- **Перестройка списка** Разрежьте, разверните заднюю половину и сплетите обе.
- **Обмен узлов парами** Разворот, где k равно двум.
- **Сложение двух чисел II** Разверните оба списка, сложите и разверните результат.

## JavaScript

```javascript
function reverseList(head) {
    let prev = null;
    let curr = head;
    while (curr) {
        const next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}
```

## Python

```python
def reverse_list(head):
    prev = None
    curr = head
    while curr:
        nxt = curr.next
        curr.next = prev
        prev = curr
        curr = nxt
    return prev
```

## PHP

```php
function reverseList(?Node $head): ?Node {
    $prev = null;
    $curr = $head;
    while ($curr !== null) {
        $next = $curr->next;
        $curr->next = $prev;
        $prev = $curr;
        $curr = $next;
    }
    return $prev;
}
```
