fedorthinks

Экспресс-курс · No. 23

Структура данных — это способ сложить данные так, чтобы то, что ты делаешь чаще всего, было быстрым. Выбрал правильную — операция мгновенная; выбрал неверную — та же задача ползёт. Big-O — язык для этой разницы: как растёт цена операции, когда растут данные.

16 понятий · схема у каждого · шпаргалка в конце

§ 01

До конкретных структур — идея, общая для всех. Структура данных — не академия. Это тот самый выбор, который чаще всего решает, будет код быстрым или мучительно медленным.

Карта курса: 16 понятий в шести группах

Карта метро: сначала видишь все линии, потом едешь по станциям.

Сверху — три базовые идеи: Data Structure, Trade-off и Big-O. Дальше шесть групп: формы роста Big-O, массив, хеш-таблица и множество, структуры с порядком (стек и очередь), структуры с формой (дерево и граф) и, наконец, как всё это выбирать на практике.

BasicsData structureTrade-offBig-OBig-O shapesO(1)O(log n)O(n)O(n²)ArrayArray / indexLinear searchInsert in middleHash mapHash mapHash functionSetOrderStack (LIFO)Queue (FIFO)ShapeTreeBalanced treeGraphUsing it wellDominant operationMap before loop
Три базовые идеи сверху и шесть групп — по ним идут главы 2–7.

Как читать курс

Паспорт: одинаковые поля у всех, поэтому их легко сравнивать.

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

Data Structure — структура данных

Ящик с инструментами, где у каждого своё гнездо, против мешка, куда всё свалено. Инструменты те же, но из ящика нужный берёшь сразу, а в мешке каждый раз роешься.

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

Та же задача бывает тривиальной или почти невозможной — смотря как лежат данные. Найти слово в отсортированном словаре — секунды; в тех же словах, сваленных как попало, — целый день. Поэтому удивительно много «сделай быстрее» на деле значит «храни в правильной форме». Структура и есть решение.

Same data1 million usersPile in a listfind id 42 → check eachMap by idfind id 42 → jump thereslow · O(n)instant · O(1)
Одни и те же данные: в куче ищем перебором, в таблице по id — прыгаем сразу.
ConceptData Structure· структура данных
Что это
Способ расположить данные в памяти под определённые операции.
Как работает

Расположение заранее решает, какие операции будут дешёвыми, а какие — дорогими.

Пример

Найти пользователя по ID: в одной структуре — мгновенно, в другой — перебор всех.

Цена
Неверный выбор не лечится «хитрой оптимизацией» потом.
Связи

Скорость структур сравнивают через Big-O; выбирают через Trade-off.

Trade-off — каждая структура это размен

Спорткар и грузовик для переезда — оба транспорт. Ты не спрашиваешь, какой лучше; ты спрашиваешь: лучше для чего?

Ни одна структура не лучшая во всём. Одна даёт мгновенный поиск, но без порядка; другая держит порядок, но медленно ищет; третья быстро добавляет, но медленно находит что-то внутри. Это и есть trade-off (размен): сила в одной операции оплачена слабостью в другой. Бесплатного обеда нет — твоя работа выбрать размен, который подходит твоему случаю.

fast atslow atArrayby index · appendsearch by value · insert midHash map / Setlookup by key · presenceno orderStack / Queueadd & take at the endsfind something inside
У каждой структуры своя сильная и своя слабая сторона.
ConceptTrade-off· размен, компромисс
Что это
Сила структуры в одной операции всегда стоит слабости в другой.
Пример

Hash Map ищет по ключу мгновенно, но не хранит порядок; Array хранит порядок, но медленно ищет по значению.

Когда думать

Каждый раз, когда выбираешь, где хранить данные: что я делаю часто, а что редко?

Связи
Размен меряется в Big-O — у каждой операции своя «форма».

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

§ 02

Чтобы сравнивать структуры, нужен язык скорости, который не зависит от компьютера. Big-O и есть этот язык: он меряет, как растёт работа, когда растут данные.

Big-O notation — рост, а не секунды

Вопрос не «сколько длится одна доставка», а «как меняется общее время, когда в маршруте не десять остановок, а десять тысяч». Форма роста, а не показания секундомера.

Нотация Big-O описывает, как растёт цена операции, когда растёт объём данных — его обозначают n. Она не смотрит на миллисекунды и конкретную машину, а ловит форму: удвоили данные — работа удвоилась, учетверилась или почти не изменилась? Эта форма и решает, останется ли код быстрым на больших данных. Поэтому Big-O — способ сравнить подходы, даже не запуская их.

data size n →workO(1)O(log n)O(n)O(n²)
Чем больше n, тем сильнее расходятся формы: O(1) и O(log n) почти лежат, O(n²) уходит вверх.
ConceptBig-O notation· «О большое», асимптотическая сложность
Что это
Описание того, как растёт работа при росте данных n.
Как работает

Отбрасываем точные секунды и детали железа, оставляем форму роста.

Пример

«Поиск по массиву — O(n)»: в десять раз больше элементов — примерно в десять раз больше работы.

Подвох
Не говорит, сколько секунд займёт; говорит, как это масштабируется.
Связи
Основные формы — в Complexity classes.

Complexity classes — формы от отличной до ужасной

Четыре способа, как растёт работа по дому: одна остаётся одним делом, сколько бы ни было вещей; другая растёт вместе с кучей; третья взрывается как куча в квадрате; четвёртая почти не растёт, даже когда куча растёт.

Несколько форм покрывают почти весь код:

  • O(1) — константа: время одно и то же при любом размере (взять элемент по индексу).
  • O(log n) — логарифм: почти не растёт, даже когда данных становится очень много (каждый шаг делит поиск пополам).
  • O(n) — линейная: растёт вместе с данными (просмотреть весь список).
  • O(n²) — квадратичная: взрывается (сравнить каждый элемент с каждым). Классика «в тестах нормально, в проде умирает».

Для десяти элементов любая структура кажется мгновенной — разница не видна. Big-O — про то, что будет, когда n станет большим. Поэтому код «вдруг» тормозит в проде: плохая форма была там всегда, просто пряталась, пока данные не выросли.

shapen = 10 (your test)n = 1 billion (prod)O(1)1 step1 stepO(log n)~3 steps~30 stepsO(n)10 steps1 billion stepsO(n²)100 steps10¹⁸ — never ends
На тестовых десяти элементах все формы похожи; на миллиарде — пропасть.
TermComplexity classes· O(1), O(log n), O(n), O(n²)
Что это
Названия типичных форм роста работы.
Пример

Индекс в массиве — O(1); поиск в сбалансированном дереве — O(log n); перебор списка — O(n); цикл в цикле — O(n²).

Подвох

O(n²) прекрасно работает на тестовых данных и молотит минутами на миллионе записей.

Правило

O(1) и O(log n) переживают масштаб; O(n) честная; O(n²) — сигнал тревоги.

Связи
Map before loop — главный способ превратить O(n²) в O(n).

Big-O меряет, как растёт работа вместе с данными, а не секунды. O(1) и O(log n) переживают масштаб; O(n) честная; O(n²) нормальна в тестах и умирает в проде.

§ 03

Самая базовая структура — массив, или список: элементы по порядку. Если точно знать, в чём он быстр и в чём медленен, всё остальное легко сравнивать с ним.

Array — упорядоченная нумерованная последовательность

Ряд пронумерованных шкафчиков: вещи стоят по порядку, у каждой свой номер — 0, 1, 2… — и к любому шкафчику можно подойти сразу по номеру.

Массив (array, list) хранит элементы по порядку, каждый на своей позиции — индексе (index), начиная с 0. Элементы лежат подряд, в известном месте памяти, поэтому прыгнуть к любой позиции по индексу — мгновенно, O(1). Массив — выбор по умолчанию для «кучи вещей по порядку», а доступ по позиции — его суперсила.

Но есть цена. Добавить или убрать элемент в конце — дёшево. А вставка или удаление в середине сдвигает всё, что после, чтобы сохранить порядок и нумерацию, — это O(n).

ann0bob1cid2dan3eve4fay5gus6hal7a[5] → straight there · O(1)index (position), starts at 0
Знаешь индекс — сразу открываешь нужный шкафчик.
newannbobciddanevefayinsert in the middle → everything after shifts · O(n)newappend at the end · cheap
Вставка в середину сдвигает всех, кто после; добавление в конец — дёшево.
StructureArray· массив, list, индекс
Что это
Элементы по порядку, у каждого номер-индекс с нуля.
Быстро
Доступ по индексу — O(1); добавление в конец — дёшево.
Медленно
Поиск по значению — O(n); вставка и удаление в середине — O(n).
Пример
Лента постов, которую читают по порядку и дополняют в конец.
Когда брать
Читаешь по позиции и в основном добавляешь в конец.
Связи

Его слабость — поиск по значению — закрывает Hash Map; Stack и Queue часто строятся поверх него.

Linear search — перебор по значению

Шкафчик 500 открываешь мгновенно. Но если знаешь только, что лежит внутри, а не номер, — придётся открывать один за другим, пока не найдёшь.

Подвох массива: он быстр, только когда знаешь индекс. Чтобы найти элемент по содержимому — «есть ли этот email в списке?» — нужно проверить каждый по очереди. Это линейный поиск (linear search), O(n): на больших списках — медленно. Ровно эту дыру закрывает следующая структура.

Is "fay" in the list?ann✗bob✗cid✗dan✗eve✗fay✓gushalcheck one by one → O(n)
Без индекса — проверяем по одному: в худшем случае весь список.
TermLinear search· линейный поиск, перебор
Что это
Проверить элементы по одному, пока не найдёшь нужный.
Цена
O(n): в десять раз больше данных — в десять раз дольше.
Пример
emails.includes(x) на списке из миллиона адресов.
Подвох

Внутри другого цикла превращается в O(n²) — тот самый «тормоз в проде».

Связи
Заменяется поиском в Hash Map или Set за O(1).

Массив — упорядоченная последовательность с индексами: мгновенный доступ по позиции, но O(n) на поиск по значению и вставку в середину.

§ 04

Если какая-то структура заслуживает быть твоей рабочей лошадкой по умолчанию — это хеш-таблица. Она закрывает главную слабость массива — поиск по значению — и делает это почти мгновенно.

Hash Map — пары ключ-значение

Телефонная книга: ищешь человека по имени и сразу получаешь номер. Ты не листаешь все записи — имя ведёт прямо к ответу.

Хеш-таблица (hash map; ещё dictionary, hash table или просто map) хранит данные как пары ключ-значение (key-value): даёшь ключ — получаешь значение. Пользователь по ID, настройка по имени, счётчик по слову — напрямую, по ключу. Массив заставляет знать позицию; хеш-таблица даёт искать по осмысленному имени.

keyvalue"ann"+598 91 111"bob"+598 92 222"eve"+598 93 333get("eve") → "+598 93 333" — no scanning
Ключ ведёт прямо к значению — без просмотра остальных записей.
StructureHash Map· хеш-таблица, dictionary, map
Что это
Пары ключ → значение с поиском по ключу.
Быстро
Поиск, добавление и удаление по ключу — фактически O(1).
Медленно
Порядка нет; найти «по значению», а не по ключу — снова перебор.
Пример
users[id], settings["theme"], подсчёт слов counts[word] += 1.
Когда брать
Постоянно ищешь что-то по идентификатору.
Связи
Скорость даёт Hash function; без значений превращается в Set.

Hash function — почему поиск мгновенный

Волшебный шкаф: по метке сразу открывается нужный ящик, другие он не проверяет. Сколько ящиков — неважно.

Магия хеш-таблицы в том, что поиск по ключу фактически O(1) — постоянное время, сколько бы элементов в ней ни было. Ключ прогоняют через хеш-функцию (hash function): она вычисляет, где лежит значение, и таблица прыгает прямо туда, а не ищет. Миллион записей или десять — найти одну одинаково быстро. Поэтому хеш-таблица и рабочая лошадка: «найди по имени» — то, что программы делают постоянно.

"eve"keyhash(key)→ slot 3012"eve" → +598 93 33334567computes where the value lives→ jumps there, no search
Хеш-функция превращает ключ в номер ячейки — таблица идёт прямо туда.
TermHash function· хеш-функция
Что это
Функция, которая по ключу вычисляет место значения в таблице.
Как работает

Один и тот же ключ всегда даёт одно и то же место — поэтому поиск не перебирает записи.

Пример
hash("eve") → ячейка 3 → там лежит номер Евы.
Связи
Сердце Hash Map и Set.

Set — есть или нет, без значений

Список гостей на входе: детали не нужны, только «да или нет» — есть это имя в списке? Проверено мгновенно, без листания.

Близкий родственник — множество (set): хеш-таблица, которая хранит только ключи, без значений, чтобы быстро отвечать на один вопрос — «это здесь есть?». Проверка присутствия и защита от дубликатов — обе O(1). Каждый раз, когда ловишь себя на переборе списка ради «я это уже видел?», множество превращает медленную O(n)-проверку в мгновенную.

has("eve")?add("bob")Setannbobeveyes · O(1)already thereno duplicate
Set отвечает «есть или нет» за O(1) и не хранит дубликатов.
StructureSet· множество
Что это
Набор уникальных ключей без значений.
Быстро
Проверка «есть ли?» и добавление — O(1); дубликаты отбрасываются сами.
Пример

seen = set() — пропускать письма, которые уже отправили; убрать повторы из списка.

Когда брать
Уникальность и быстрые проверки присутствия.
Связи
Это Hash Map без значений.

Хеш-таблица находит значение по ключу фактически за O(1) — рабочая лошадка для поиска по идентификатору. Её родственник Set так же мгновенно отвечает «это здесь есть?».

§ 05

Иногда порядок, в котором вынимаешь вещи, важен не меньше того, что ты хранишь. Две структуры закрепляют два естественных порядка — и разница между ними в одном простом правиле.

Stack — последним пришёл, первым ушёл (LIFO)

Стопка тарелок: кладёшь сверху и берёшь сверху. Последняя положенная — первая, которую возьмёшь.

Стек (stack) работает по правилу LIFO — last in, first out (последним пришёл — первым ушёл). Добавляешь (push) и убираешь (pop) с одного конца, поэтому первым выходит самый свежий элемент. Это подходит всему, что вкладывается или разматывается в обратном порядке: история отмены (undo), кнопка «назад», стек вызовов программы (call stack). Обе операции — O(1).

page 1page 2page 3page 4← top (newest)pushpoplast in, first out
Push кладёт наверх, pop снимает сверху: самый свежий выходит первым.
StructureStack· стек, LIFO
Что это
Добавляем и убираем только с вершины.
Быстро
push и pop — O(1).
Пример
Undo в редакторе, кнопка «назад» в браузере, call stack.
Когда брать
Нужен порядок «самое свежее первым» или «отмени последнее».
Связи
Зеркало Queue: те же данные, другой конец для выдачи.

Queue — первым пришёл, первым ушёл (FIFO)

Очередь в кассу: обслуживают в порядке прихода, новые встают в хвост, следующим идёт тот, кто впереди.

Очередь (queue) работает по правилу FIFO — first in, first out (первым пришёл — первым ушёл). Добавляешь в хвост (enqueue), забираешь спереди (dequeue), поэтому элементы обрабатываются в порядке прихода. Это структура для честности и порядка: задачи на обработку, ждущие работы, запросы по очереди — та же идея, что у очереди сообщений из курса про очереди. Как и у стека, операции — O(1).

job 1job 2job 3job 4dequeueenqueuefront (oldest)back (newest)first in, first out
Новые встают в хвост, обслуживают спереди — в порядке прихода.
StructureQueue· очередь, FIFO
Что это
Добавляем в хвост, забираем с головы.
Быстро
enqueue и dequeue — O(1).
Пример
Задачи для фоновых воркеров, запросы, которые обслуживают по очереди.
Когда брать
Порядок прихода должен стать порядком обработки.
Связи
Зеркало Stack; в большом масштабе вырастает в message queue.

Правило определяет поведение

Одна и та же куча работы, две двери: одна отдаёт самое свежее, другая — самое старое. Один выбор — с какого конца брать — меняет всё дальше.

Стек и очередь хранят одно и то же; вся разница — с какого конца забираешь. Выбирать между ними — значит выбирать порядок, в котором работа к тебе возвращается: обратный и вложенный (стек) или честный и последовательный (очередь). Маленькое решение с большим эффектом: угадай порядок — и остальная логика встанет на место.

in: 1, 2, 3same itemsStacktake from topQueuetake from frontout: 3, 2, 1newest firstout: 1, 2, 3arrival order
Положили 1, 2, 3: стек отдаст 3, 2, 1, очередь — 1, 2, 3.

Stack — последним пришёл, первым ушёл: отмена, вложенность, разворот. Queue — первым пришёл, первым ушёл: честная обработка по порядку. Данные те же; конец, с которого забираешь, — вся разница.

§ 06

Две последние структуры — для данных, которые не лежат в одну линию, а имеют форму: ветвятся в иерархию или сплетаются в сеть. Так моделируют связи между вещами.

Tree — иерархия

Семейное древо или оргструктура компании: одно наверху, дальше ветвится вниз на детей, у тех свои дети. Расходится веером и никогда не замыкается обратно.

Дерево (tree) хранит данные как иерархию: сверху корень (root), от него ветвятся узлы (nodes) с детьми, и так вниз. Оно моделирует всё вложенное — папки в папках, комментарии и ответы на них, меню категорий. Форма дерева повторяет реальную вложенность, поэтому оно естественный выбор, когда данные — «вещи внутри вещей». Большинство иерархий в софте — деревья под капотом.

/ (root)docssrccv.pdfnotesapp.tslibnode with children· no loops back
Файловая система — дерево: корень, папки-узлы, файлы-листья, без петель.
StructureTree· дерево; root, node
Что это
Иерархия: у каждого узла есть дети, но нет петель назад.
Пример
Папки и файлы, ветка комментариев, дерево категорий в магазине.
Когда брать
Данные устроены как «вещи внутри вещей».
Подвох

Если впихнуть иерархию в плоский список, придётся склеивать её обратно руками.

Связи

Упорядоченное и ровное дерево — Balanced tree; дерево — частный случай Graph.

Balanced tree — поиск за O(log n)

Ищешь имя в телефонной книге: открываешь середину, потом середину нужной половины, и снова. Каждый шаг выкидывает половину оставшегося.

Хорошо организованное, сбалансированное (balanced) дерево даёт мощное свойство: в нём можно искать за O(log n), каждый раз деля пространство пополам — как ищешь слово в словаре, не читая каждую страницу. Логарифм почти не растёт, даже когда данные взрываются: миллиард элементов — всего около тридцати шагов. Так базы данных держат поиск быстрым на огромном масштабе: упорядоченные данные в форме дерева, поиск делением пополам.

50257512376288find 6262 > 50 → right62 < 75 → lefteach step drops half · 1 billion items ≈ 30 steps · O(log n)
Ищем 62: больше 50 — направо, меньше 75 — налево. Каждый шаг отбрасывает половину.
StructureBalanced tree· сбалансированное дерево поиска
Что это
Упорядоченное дерево одинаковой глубины со всех сторон.
Как работает

На каждом узле сравниваем и уходим в одну половину — вторую больше не смотрим.

Быстро
Поиск — O(log n): миллиард элементов ≈ 30 шагов.
Пример
Индексы в базах данных: упорядоченные ключи в форме дерева.
Связи
Частный случай Tree; в отличие от Hash Map хранит порядок.

Graph — сеть связей

Карта городов, соединённых дорогами, или люди, связанные дружбой: всё связано со всем в любом узоре, без верха и низа.

Граф (graph) — самая общая форма: узлы (nodes), соединённые рёбрами (edges), в любом узоре, включая петли. Он моделирует сети — социальные связи, дороги между городами, ссылки между страницами, зависимости между задачами. Если данные по сути «вещи, связанные с другими вещами», а не иерархия и не линия, — это граф. Многие сложные задачи — кратчайший маршрут, кто с кем связан — в основе графовые.

AnnBobEveMaxKimLeaedgenodes + edges · any pattern · loops allowed (Bob–Kim–Eve)
Узлы и рёбра в любом узоре; петли разрешены — в отличие от дерева.
StructureGraph· граф; node, edge
Что это
Узлы и рёбра между ними — без иерархии, петли разрешены.
Пример

Друзья в соцсети, дороги между городами, ссылки между страницами, зависимости задач.

Типичные задачи
Кратчайший маршрут; кто с кем связан.
Когда брать
Данные — сеть, а не линия и не иерархия.
Связи
Tree — это граф без петель с одним корнем.

Tree моделирует иерархию и, если сбалансировано, ищет за O(log n) делением пополам. Graph моделирует сеть связей. Бери их, когда у данных есть форма — вложенная или связанная.

§ 07

С нуля эти структуры почти никто не пишет — они уже есть в твоём языке. Навык — выбрать правильную под задачу и знать Big-O настолько, чтобы почуять плохой выбор до того, как он укусит.

Dominant operation — выбирай по частой операции

Кухню собирают под то, что готовят каждый вечер, а не под то, что эффектно смотрится.

Практическое решение почти всегда одно: какую операцию я делаю чаще всего, и какая структура делает именно её быстрой? Постоянный поиск по ключу — Hash Map. Порядок прихода — Queue. В основном чтение по позиции — Array. Пусть доминирующая операция (dominant operation) выбирает структуру, а с медленностью в редких операциях смирись. Оптимизируй частый случай — структура и есть способ.

What do Ido most often?look up by keyHash mapis it present? dedupeSetread by position, appendArraynewest first, undoStackin arrival orderQueuethings inside thingsTreethings linked to thingsGraph
Назови свою частую операцию — она сама укажет на структуру.
PracticeDominant operation· выбор по частой операции
Идея
Структуру выбирает самая частая операция, а не привычка.
Пример

Тысячи поисков пользователя по ID в секунду → Hash Map, даже если порядок теряется.

Цена
Редкие операции станут медленнее — это осознанный размен.
Связи
Применение Trade-off на практике.

Map before loop — сначала таблица, потом цикл

Прежде чем искать человека в толпе по лицам, проверь, нет ли списка гостей: список отвечает мгновенно на то, что поиск делает медленно.

Очень частый и очень полезный ход: когда ловишь себя на переборе списка, чтобы найти или сопоставить что-то, — O(n)-цикл или, хуже, O(n²)-цикл внутри цикла, — спроси, не сделает ли это мгновенным хеш-таблица или множество. Замена вложенного поиска на поиск по таблице — одно из самых частых реальных ускорений. Чинит обычно структура, а не более быстрый цикл.

before: loop inside a loopfor a in orders1 000 itemsfor u in usersscan all · 1 000O(n²)1 000 000 checksafter: build a map once, then look upbyId = map(users)once · 1 000byId[a.userId]O(1) eachO(n)~2 000 steps
Было: цикл в цикле, миллион проверок. Стало: один раз строим таблицу, дальше O(1) на каждый поиск.
PracticeMap before loop· таблица вместо вложенного цикла
Проблема

Для каждого элемента перебираем другой список — O(n²), «тормоз в проде».

Идея

Один раз построить Hash Map или Set, дальше искать в нём за O(1).

Пример

Вместо users.find(u => u.id === order.userId) в цикле по заказам — заранее byId и byId[order.userId].

Цена
Немного памяти под таблицу.
Связи
Превращает Linear search внутри цикла в O(1).
Прежде чем выбрать структуру
  • Какая операция главная — поиск, порядок, добавление/удаление?
  • Поиск по ключу? Hash Map (или Set для «есть ли») — O(1).
  • Доступ по позиции и добавление в конец? Подходит Array.
  • Важен порядок выдачи — самое свежее (Stack) или самое старое (Queue)?
  • Вложенные или связанные данные — Tree или Graph?
  • Какой Big-O на большом n — и нет ли где-то скрытого O(n²)?
Признаки, что ты выбираешь хорошо
  • Выбираешь структуру по частой операции, а не по привычке.
  • Берёшь Hash Map или Set вместо перебора списка, чтобы найти или убрать дубликаты.
  • Можешь назвать Big-O своего горячего пути — и в нём нет скрытого O(n²).
  • Берёшь Stack или Queue, когда важен порядок выдачи, и Tree или Graph, когда у данных есть форма.
  • Код остаётся быстрым, когда данные растут, — потому что ты выбрал форму раньше, чем масштаб её вскрыл.

Как всё связано

Генеалогия инструментов: каждый следующий закрывает слабость предыдущего.

Big-O — линейка, которой меряют все структуры. Hash Map закрывает слабость Array (поиск по значению), Set — это Hash Map без значений. Stack и Queue — последовательность с правилом «забирай только с конца». Tree даёт иерархию, а сбалансированное — поиск за O(log n); Graph снимает последнее ограничение и разрешает любые связи.

Big-Omeasures every opArrayby indexHash mapby keySetpresenceStack / Queueorder ruleTreebalanced: log nGraphany linksfind fastkeys onlytake only from an endallow loops
Big-O меряет всех; каждая структура отвечает на слабость или ограничение соседней.

Шпаргалка: все 16 понятий

Основы

Data Structureструктура данных

Способ расположить данные так, чтобы частые операции были быстрыми.

Trade-offразмен

Сила в одной операции стоит слабости в другой.

Big-O

Big-O notation«О большое»

Как растёт работа, когда растут данные n.

Complexity classesO(1), O(log n), O(n), O(n²)

Константа, логарифм, линейная, квадратичная. O(n²) — тревога.

Массив

Arrayмассив, list

По порядку, с индексом: по позиции O(1), вставка в середину O(n).

Linear searchперебор

Ищем по значению, проверяя по одному. O(n).

Хеш-таблица

Hash Mapdictionary, map

Ключ → значение, поиск по ключу фактически O(1). Без порядка.

Hash functionхеш-функция

Вычисляет по ключу, где лежит значение, — поэтому без перебора.

Setмножество

Только ключи: «есть ли?» за O(1), без дубликатов.

Порядок

StackLIFO

Последним пришёл — первым ушёл. Undo, «назад», call stack.

QueueFIFO

Первым пришёл — первым ушёл. Обработка в порядке прихода.

Форма

Treeдерево

Иерархия: корень, узлы, дети, без петель.

Balanced treeсбалансированное дерево

Поиск делением пополам — O(log n). Так работают индексы баз.

Graphграф

Узлы и рёбра в любом узоре. Сети, маршруты, зависимости.

Как выбирать

Dominant operationчастая операция

Структуру выбирает самая частая операция.

Map before loopтаблица вместо цикла

Перед вложенным перебором — построй Hash Map или Set.

Ты не строишь их с нуля — ты их выбираешь. Подбирай структуру под частую операцию, тянись к таблице раньше, чем к циклу, и пусть Big-O предупредит тебя раньше, чем масштаб.

Конец экспресс-курса · 7 глав · 16 понятий

Дальше — практика: найди в своём коде место, где ты идёшь циклом по списку, чтобы найти или сопоставить что-то, и замени его поиском по хеш-таблице или множеству — потом посмотри, насколько это быстрее на больших данных. Потом для одной медленной операции выведи её Big-O и спроси, меняет ли другая структура форму. Но держи главную мысль: правильная форма делает сложное простым. Большинство ответов на «почему медленно?» — «оно лежит не в той структуре», а выбрать правильную — самая дешёвая скорость, которую ты когда-либо купишь.