Слияние связных списков

Базовый Algorithms
≈ 2 мин чтения

Слияние двух отсортированных цепочек

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

Один проход по двум спискам

Node dummy = new Node(0);
Node tail = dummy;

while (first != null && second != null) {
    if (first.value <= second.value) {
        tail.next = first;
        first = first.next;
    } else {
        tail.next = second;
        second = second.next;
    }

    tail = tail.next;
}

tail.next = first != null ? first : second;
Node merged = dummy.next;

tail всегда указывает на последний узел уже собранной части результата. После выбора меньшего узла соответствующая ссылка first или second сдвигается вперёд. Когда один список заканчивается, остаток второго уже отсортирован и его можно присоединить целиком.

Зачем нужен dummy-узел

Временный dummy убирает отдельный случай для первого добавленного элемента: у результата с самого начала есть узел, к которому можно присоединять выбранные элементы через tail.next. Настоящая голова результата находится в dummy.next.

Если в первом списке n узлов, а во втором m, каждый исходный узел обрабатывается не более одного раза. Поэтому время работы — O(n + m). Алгоритм переиспользует существующие узлы и, кроме служебного dummy, не создаёт копий элементов.

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