Что такое связный список (Linked List) как структура данных?

Ответ

Связный список — это линейная структура данных, состоящая из последовательности узлов (Nodes), где каждый узел хранит:

  1. Данные (value).
  2. Ссылку (указатель) на следующий узел (next).

Реализация узла на Swift:

class Node<T> {
    var value: T
    var next: Node? // Ссылка на следующий узел

    init(value: T, next: Node? = nil) {
        self.value = value
        self.next = next
    }
}

// Создание списка: 1 -> 2 -> 3
let node3 = Node(value: 3)
let node2 = Node(value: 2, next: node3)
let head = Node(value: 1, next: node2) // Голова списка

Основные виды связных списков:

  • Односвязный (Singly Linked): Узел ссылается только на следующий.
  • Двусвязный (Doubly Linked): Узел содержит ссылки на next и previous.
  • Кольцевой (Circular): Последний узел ссылается на первый.
Сравнение с массивом (Array): Операция Связный список Массив
Доступ по индексу O(n) (медленно) O(1) (быстро)
Вставка в начало O(1) (быстро) O(n) (медленно)
Удаление из начала O(1) (быстро) O(n) (медленно)
Использование памяти Больше (хранит ссылки) Меньше (только данные)

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

Ответ 18+ 🔞

А, связный список, ну это ж классика, блядь! Представь себе цепочку, где каждый звенышко — это как отдельный чувак, который держит в одной руке свою ценную хуйню (данные), а другой рукой хватает следующего такого же чувака за шкирку. Вот и вся структура, ёпта!

Каждый узел — это просто бандит с двумя карманами:

  1. В одном — его ценность (value), что он там хранит, похуй.
  2. Во втором — адресок на следующего такого же бандита (next). А если он последний в цепочке, то в кармане пусто, nil, пиздец и точка.

Вот как это на Swift выглядит, смотри не обосрись:

class Node<T> {
    var value: T
    var next: Node? // Вот эта ссылочка, которая тянется к следующему

    init(value: T, next: Node? = nil) {
        self.value = value
        self.next = next
    }
}

// Собираем цепочку: 1 -> 2 -> 3
let node3 = Node(value: 3) // Третий, ему не за кого хвататься, он крайний
let node2 = Node(value: 2, next: node3) // Второй, хватает третьего
let head = Node(value: 1, next: node2) // Голова, главный, он цепляет второго

А ещё эти списки бывают разные, как люди в подъезде:

  • Односвязный — как скромняга, знает только того, кто перед ним. Только next.
  • Двусвязный — уже похуистичнее, знает и того, кто перед ним, и того, кто сзади (next и previous). Удобно, если надо дать назад.
  • Кольцевой — это вообще ебушки-воробушки, когда последний хватает за шкирку первого, и они все по кругу бегают, пиздец.

А теперь главное: чем это лучше или хуже обычного массива? Сейчас разберём, блядь.

Что делаем Связный список Массив (Array)
Найти по номеру O(n) — иди по цепочке, всех перещупай, медленно. O(1) — прыгнул сразу на нужную ячейку, быстро!
Впихнуть в начало O(1) — взял нового чувака, дал ему адресок старой головы, и всё, ты теперь голова! Быстро! O(n) — блядь, всем остальным придётся сдвигаться, чтобы место освободить. Медленно!
Выкинуть из начала O(1) — сказал голове: "Ты свободен!", и назначил головой второго. Быстро! O(n) — опять всех двигать, медленно!
Память Больше — каждый чувак хранит ещё и ссылку, лишний байтик. Меньше — хранят только свои данные, компактно.

Так когда эту хуйню использовать? Да когда тебе похуй на быстрый поиск по индексу, но надо часто в начале цепочки что-то пихать или выкидывать! Очереди, стеки — их родная стихия. А если тебе надо постоянно прыгать туда-сюда по индексам — иди нахуй с твоим списком, бери массив.