Модуль 9 · Урок 34 із 58

Search і sort: linear, binary, bisect та stability

Binary search швидкий не тому, що код короткий, а тому, що кожен крок відкидає частину впорядкованого простору. Якщо sorted invariant не доведений або insertion домінує, очікувана перевага зникає.

Linear searchBinary searchbisectStable sort

Безпечна algorithm-практика модуля 9

Пакет містить тільки synthetic tasks і graph, deterministic operation counts, локальний Python CLI, tests та read-only verifier. Жодних персональних чи production-даних, credentials, мережевих викликів, machine paths або зовнішніх залежностей.

Завантажити практичний пакет →

Linear search — чесний baseline

Послідовний пошук не потребує попереднього порядку і може завершитися на першому match. Worst case відвідує всі n елементів. Він часто правильний для малого одноразового input або predicate, який не має sortable key.

def find_case(rows, case_id):
    for row in rows:
        if row["id"] == case_id:
            return row
    return None

Контракт має сказати, що робити з duplicate IDs: повернути перший, усі, або відхилити input.

Binary search потребує sorted invariant

На кожному кроці порівнюємо target із middle element і лишаємо половину search interval. Для коректності треба чітко визначити half-open межі [lo, hi), termination condition і поведінку на missing target. Якщо дані сортуються перед кожним одиничним lookup, повна ціна включає sort.

1Перевірити порядок і key contract.
2Взяти middle без виходу за межі.
3Звузити [lo, hi), не повторюючи стан.
4Повернути exact match або explicit missing.

bisect: insertion point не дорівнює match

bisect_left повертає позицію, де target можна вставити перед рівними keys зі збереженням порядку. Після цього все одно треба перевірити index < len(data) і equality. Документація окремо попереджає: insort має linear insertion cost, бо переміщення елементів list домінує над logarithmic search.

Search structure залежить від workload

Для багатьох exact lookups без range semantics часто доречніший dict. Для range queries та вже впорядкованих immutable snapshots — sorted sequence плюс bisect.

Python sort: key один раз і stable ties

sorted(iterable, key=...) створює новий list; list.sort() змінює list і повертає None. Key function викликається один раз для кожного record. Stable sort зберігає relative order записів із однаковим key, тому tie policy може спиратися на вхідний порядок лише якщо він сам задокументований і deterministic.

ordered = sorted(
    cases,
    key=lambda row: (row["priority"], row["created_seq"], row["id"]),
)

Explicit tuple key робить business priority видимою. ID як останній tie-breaker прибирає випадковість, якщо input order не є контрактом.

Search/sort QA

  • Empty, single, first, middle, last і missing target.
  • Duplicate key policy та left/right boundary.
  • Already sorted, reverse і partially ordered input.
  • Key із None, mixed types або invalid values має explicit validation.
  • Input mutation перевіряється окремо.
  • Result order порівнюється повністю, не лише як set.

Методичні джерела

Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть лише на офіційну документацію та Python Wiki.

Практична перевірка · урок 34 з 47

Закріпіть матеріал уроку

Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.

1. Коли linear search є коректним і практичним baseline замість binary search?
2. Який invariant необхідний для коректного binary search по list із case IDs?
3. bisect_left повернув index, рівний len(keys). Що має зробити exact-match helper?