2.2.3.8 — последняя статья раздела: одномерная идея, применённая к двум измерениям сразу2.2.3.8 — the section's last article: a one-dimensional idea applied to two dimensions at once

Поиск в Отсортированной МатрицеSaddleback Search

Все предыдущие алгоритмы этой темы (2.2.3.1-2.2.3.7) работают с одномерным диапазоном [lo, hi]. Saddleback search решает принципиально другую задачу: дана матрица, где каждая строка отсортирована по возрастанию слева направо и каждый столбец — сверху вниз; нужно найти x или доказать его отсутствие, не читая всю матрицу. Название — от седловидной формы траектории поиска на сетке.Every earlier algorithm in this topic (2.2.3.1-2.2.3.7) works with a one-dimensional range [lo, hi]. Saddleback search solves a fundamentally different problem: given a matrix where every row is sorted ascending left to right and every column top to bottom, find x or prove its absence without reading the whole matrix. The name comes from the saddle-shaped path the search traces across the grid.

O(n+m) in-place не одномерная задачаnot a 1D problem

Идея и инвариантIdea & invariant

Поиск начинается в правом верхнем углу матрицы — единственной клетке, про которую сразу известно и то, что она максимальна в своей строке, и то, что она минимальна в своём столбце. Пусть текущая клетка (r, c) содержит значение v. Если v == x — готово. Если v > x, то x не может находиться нигде в столбце c (все элементы столбца ниже (r,c) ещё больше, так как столбец отсортирован по возрастанию вниз) — столбец c целиком отбрасывается сдвигом c влево. Если v < x, аналогично отбрасывается строка r сдвигом r вниз (все элементы левее в этой строке ещё меньше). Инвариант — то, что "если x есть в матрице, он лежит в оставшемся прямоугольнике [0..rows) × [0..c]" — сохраняется на каждом шаге, а сам указатель либо движется влево, либо вниз, то есть суммарно делает не больше rows + cols шагов.The search starts at the matrix's top-right corner — the one cell known immediately to be both the maximum in its row and the minimum in its column. Let the current cell (r, c) hold value v. If v == x, done. If v > x, x can't be anywhere in column c (every element below (r,c) in that column is even larger, since the column is sorted ascending downward) — the whole column c is discarded by moving c left. If v < x, symmetrically row r is discarded by moving r down (every element to the left in that row is even smaller). The invariant — "if x is in the matrix, it lies in the remaining rectangle [0..rows) × [0..c]" — is preserved at every step, and the pointer itself either moves left or down, so it takes at most rows + cols steps total.

поиск x=11, старт в правом верхнем углу 15 15 > 11 — отбросить столбец, ← влево 10 10 < 11 — отбросить строку, ↓ вниз 11 11 = 11 — найдено! путь: только влево или только вниз — не более rows+cols шагов, никогда не читает всю матрицу

каждый шаг детерминированно отбрасывает целую строку или столбец — прямой аналог отбрасывания половины диапазона в 1D-поискеeach step deterministically discards a whole row or column — a direct analogue of discarding half the range in 1D search

std::optional<std::pair<int,int>> saddleback_search(
    const std::vector<std::vector<int>>& m, int x) {
    if (m.empty() || m[0].empty()) return std::nullopt;
    int r = 0, c = (int)m[0].size() - 1;  // правый верхний угол// top-right corner
    while (r < (int)m.size() && c >= 0) {
        if (m[r][c] == x) return std::make_pair(r, c);
        else if (m[r][c] > x) --c;   // отбросить столбец// discard the column
        else ++r;                   // отбросить строку// discard the row
    }
    return std::nullopt;
}

Демонстрация вживуюLive demonstration

Жёлтая клетка — текущая позиция указателя. Затемнённые клетки — уже отброшенные строки/столбцы. Указатель всегда стартует в правом верхнем углу и движется только влево или вниз.The yellow cell is the pointer's current position. Dimmed cells are already-discarded rows/columns. The pointer always starts at the top-right corner and moves only left or down.

шагов:steps: 0

нажмите «Вперёд» или «Играть», чтобы начатьpress "Forward" or "Play" to begin

Почему нельзя добиться O(log(n·m))Why O(log(n·m)) isn't achievable

Заманчиво ожидать логарифмическую сложность по аналогии с 1D бинарным поиском, но у двумерной отсортированности гораздо более слабая структура: в отличие от одномерного массива, где сравнение с одним элементом делит весь оставшийся набор кандидатов пополам, сравнение с одной клеткой матрицы устраняет только одну строку или один столбец — то есть O(n) или O(m) кандидатов из O(n·m), а не половину. Можно показать (через явные примеры "тяжёлых" матриц), что Ω(n+m) сравнений необходимы в худшем случае для любого алгоритма, работающего только через сравнение элементов — O(n+m) этого алгоритма, следовательно, асимптотически оптимально, не будучи логарифмическим.It's tempting to expect logarithmic complexity by analogy with 1D binary search, but two-dimensional sortedness has a much weaker structure: unlike a 1D array, where comparing against one element halves the entire remaining candidate set, comparing against one matrix cell eliminates only one row or one column — that is, O(n) or O(m) candidates out of O(n·m), not half. It can be shown (via explicit "hard" matrix examples) that Ω(n+m) comparisons are necessary in the worst case for any algorithm working only through element comparison — this algorithm's O(n+m) is therefore asymptotically optimal, without being logarithmic.

Где встречаетсяWhere it shows up

01

Классическая задача на собеседованиях (LeetCode "Search a 2D Matrix II")A classic interview problem (LeetCode "Search a 2D Matrix II")

Регулярно встречается на технических собеседованиях именно потому, что проверяет способность распознать структуру задачи, отличную от привычного 1D бинарного поиска, и вывести правильный инвариант (отбрасывание строки/столбца) с нуля.Regularly appears in technical interviews precisely because it tests the ability to recognize a problem structure different from familiar 1D binary search, and to derive the correct invariant (discarding a row/column) from scratch.

02

Поиск в разреженных динамических таблицах (например, таблицах Юнга)Searching sparse dynamic tables (e.g. Young tableaux)

Таблицы Юнга — структура данных с ровно тем же инвариантом упорядоченности (строки и столбцы возрастают), используемая в некоторых приоритетных очередях и комбинаторных алгоритмах; saddleback search — стандартная операция поиска в этой структуре.Young tableaux are a data structure with exactly this ordering invariant (rows and columns increase), used in some priority queues and combinatorial algorithms; saddleback search is the standard search operation on this structure.