Files
hello-algo/ru/docs/chapter_searching/binary_search.md
Yudong Jin 22b3b568ef fix Ru translation (#1894)
* docs(ru): replace prose quotes with guillemets

* docs(ru): replace prose semicolons with periods

* docs(ru): align animation title forms

* docs(ru): align figure and table references
2026-04-14 18:10:12 +08:00

8.9 KiB
Raw Blame History

Двоичный поиск

Двоичный поиск (binary search) - это эффективный алгоритм поиска, основанный на стратегии «разделяй и властвуй». Он использует упорядоченность данных, сокращая на каждом шаге область поиска вдвое, пока не будет найден целевой элемент или пока интервал поиска не опустеет.

!!! question

Дан массив `nums` длины $n$, элементы которого расположены в порядке возрастания и не повторяются. Найдите и верните индекс элемента `target` в этом массиве. Если массив не содержит этого элемента, верните $-1$ . Пример показан на рисунке ниже.

Пример данных для двоичного поиска

Как показано на рисунке ниже, сначала инициализируем указатели i = 0 и j = n - 1 , которые указывают на первый и последний элементы массива и задают интервал поиска [0, n - 1] . Обратите внимание: квадратные скобки обозначают замкнутый интервал и включают граничные значения.

Далее в цикле выполняются следующие два шага.

  1. Вычислить индекс середины m = \lfloor {(i + j) / 2} \rfloor , где \lfloor \: \rfloor означает операцию округления вниз.
  2. Сравнить nums[m] и target , после чего возможны три случая.
    1. Если nums[m] < target , это означает, что target находится в интервале [m + 1, j] , поэтому выполняется i = m + 1 .
    2. Если nums[m] > target , это означает, что target находится в интервале [i, m - 1] , поэтому выполняется j = m - 1 .
    3. Если nums[m] = target , значит, элемент target найден, поэтому возвращается индекс m .

Если массив не содержит целевой элемент, область поиска в итоге сузится до пустого интервала. В этом случае возвращается -1 .

=== "<1>" Процесс двоичного поиска

=== "<2>" binary_search_step2

=== "<3>" binary_search_step3

=== "<4>" binary_search_step4

=== "<5>" binary_search_step5

=== "<6>" binary_search_step6

=== "<7>" binary_search_step7

Стоит отметить, что поскольку и i , и j имеют тип int , то сумма i + j может выйти за пределы диапазона типа int. Чтобы избежать переполнения, обычно используют формулу m = \lfloor {i + (j - i) / 2} \rfloor для вычисления середины.

Код приведен ниже:

[file]{binary_search}-[class]{}-[func]{binary_search}

Временная сложность равна $O(\log n)$ : в цикле двоичного поиска интервал каждый раз сокращается вдвое, поэтому число итераций равно \log_2 n .

Пространственная сложность равна $O(1)$ : указатели i и j занимают константный объем памяти.

Методы представления интервалов

Помимо описанного выше двойного замкнутого интервала, часто используется и левозамкнутый правооткрытый интервал, который задается как [0, n) , то есть левая граница включается, а правая - нет. В этом представлении интервал [i, j) пуст, когда i = j .

На основе этого представления можно реализовать двоичный поиск с той же функциональностью:

[file]{binary_search}-[class]{}-[func]{binary_search_lcro}

Как показано на рисунке ниже, в этих двух вариантах представления интервала различаются инициализация, условие цикла и операция сужения интервала в алгоритме двоичного поиска.

Поскольку в записи «двойной замкнутый интервал» обе границы являются закрытыми, операции сужения интервала при помощи указателей i и j тоже получаются симметричными. Из-за этого в таком варианте сложнее допустить ошибку, поэтому обычно рекомендуется использовать именно запись «двойной замкнутый интервал».

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

Преимущества и ограничения

Двоичный поиск показывает хорошие результаты и по времени, и по памяти.

  • Двоичный поиск очень эффективен по времени. На больших объемах данных логарифмическая временная сложность дает заметное преимущество. Например, когда размер данных n = 2^{20} , линейный поиск потребует 2^{20} = 1048576 итераций, тогда как двоичный поиск выполнится всего за \log_2 2^{20} = 20 итераций.
  • Двоичный поиск не требует дополнительной памяти. По сравнению с алгоритмами поиска, которым нужно внешнее пространство (например, с хеш-поиском), двоичный поиск заметно экономнее по памяти.

Однако двоичный поиск подходит не для всех ситуаций, и основные причины таковы.

  • Двоичный поиск применим только к упорядоченным данным. Если входные данные неупорядочены, специально сортировать их ради двоичного поиска невыгодно. Это связано с тем, что временная сложность алгоритмов сортировки обычно составляет O(n \log n) , что выше, чем у линейного и двоичного поиска. Если элементы приходится часто вставлять, то для сохранения порядка в массиве их нужно помещать в конкретные позиции, а это требует O(n) времени и тоже обходится дорого.
  • Двоичный поиск применим только к массивам. Для него нужен скачкообразный доступ к элементам, а в связном списке такой доступ малоэффективен, поэтому двоичный поиск не подходит для списков и структур данных, построенных на их основе.
  • При малом объеме данных линейный поиск работает лучше. В линейном поиске на каждом шаге нужна всего одна операция сравнения. В двоичном поиске требуется 1 сложение, 1 деление, от 1 до 3 сравнений и еще 1 сложение или вычитание, то есть всего от 4 до 6 элементарных операций. Поэтому при небольшом n линейный поиск может оказаться быстрее двоичного.