Итеративный бинарный поиск на C, C++ и Python
Кратко
Итеративный бинарный поиск быстро находит элемент в отсортированном массиве за O(log n) сравнений и использует O(1) дополнительной памяти в итеративной реализации. В этой статье вы найдёте понятные объяснения, рабочие реализации на C, C++ и Python, тест-кейсы, когда метод не применим, альтернативы и практические советы по отладке.
О чём эта статья
- Понятие бинарного поиска и логика работы.
- Полные примеры кода на C, C++ и Python (итеративный вариант).
- Когда и почему итеративный вариант экономит память.
- Частые ошибки, тест-кейсы и чек-листы для интервью/инженеров.

Что такое бинарный поиск?
Бинарный поиск — это алгоритм для поиска позиции элемента в отсортированном массиве. Он последовательно делит диапазон поиска пополам, сравнивая искомое значение с медианой текущего диапазона.
Ключевая идея: если искомое значение больше среднего элемента, то искать нужно только в правой половине; если меньше — в левой. При каждом шаге размер диапазона уменьшается примерно вдвое, поэтому время работы составляет O(log n).
Кратко про ограничения и предпосылки:
- Массив должен быть строго или нестрого отсортирован по возрастанию (или по убыванию — требуется соответствующая адаптация сравнения).
- Для корректной работы нужны индексируемые структуры (массивы, списки с доступом по индексу).
- При наличии дубликатов бинарный поиск вернёт некоторую позицию совпадения; для поиска первого/последнего вхождения требуются модификации.
Ментальная модель (как думать о бинарном поиске)
- Представьте, что вы ищете слово в словаре: открываете книгу на середине и решаете, в какую половину дальше смотреть.
- Каждый шаг — это отсечение половины возможных позиций.
- Если на каждом шаге вы сокращаете пространство в 2 раза, то число шагов ≈ log2(n).
Почему выбирать итеративную реализацию?
- Итеративный вариант использует постоянный объем дополнительной памяти: O(1).
- Рекурсивный вариант использует стек вызовов и потребляет O(log n) памяти (в глубине рекурсии), что может быть критично в ограниченной среде.
- Итеративная версия проще контролируется для условий выхода и стабильней в ресурсозависимых средах.
Практические замечания
- Безопасный расчёт mid: используйте mid = low + (high - low) / 2 или mid = low + (high - low) >> 1, чтобы избежать переполнения при больших индексах.
- Если массив отсортирован по убыванию, инвертируйте сравнения.
- Для поиска первого/последнего вхождения выбирайте сдвиги границ без немедленного выхода при совпадении.
Итеративная реализация: C (полная программа)
Ниже — рабочий пример на C. Функция binarySearch возвращает индекс найденного элемента или -1, если элемент отсутствует.
#include
int binarySearch(int arr[], int arr_size, int search_number) {
int low = 0;
int high = arr_size - 1;
while (low <= high) {
int mid = low + (high - low) / 2; // безопасный расчёт
if (arr[mid] == search_number)
return mid;
else if (arr[mid] > search_number)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
int main() {
int arr[100], i, arr_size, search_number;
printf("Enter number of elements: ");
if (scanf("%d", &arr_size) != 1) return 1;
if (arr_size < 0 || arr_size > 100) return 1;
printf("Please enter numbers: ");
for (i = 0; i < arr_size; i++) {
if (scanf("%d", &arr[i]) != 1) return 1;
}
printf("Enter number to be searched: ");
if (scanf("%d", &search_number) != 1) return 1;
i = binarySearch(arr, arr_size, search_number);
if (i == -1)
printf("Number not present\n");
else
printf("Number is present at position %d\n", i + 1);
return 0;
} Важно: код предполагает, что вводимые элементы уже отсортированы. Если это не так, перед поиском массив нужно отсортировать.
C++: минимальные отличия и советы
В C++ удобно использовать iostream и контейнер std::vector. Пример замены scanf/printf на cin/cout:
#include
#include
using namespace std;
int binarySearch(const vector& arr, int search_number) {
int low = 0, high = (int)arr.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] == search_number) return mid;
else if (arr[mid] > search_number) high = mid - 1;
else low = mid + 1;
}
return -1;
}
int main() {
int arr_size, search_number;
cout << "Enter number of elements: ";
if (!(cin >> arr_size)) return 1;
vector arr(arr_size);
cout << "Please enter numbers: ";
for (int i = 0; i < arr_size; ++i) cin >> arr[i];
cout << "Enter number to be searched: ";
cin >> search_number;
int result = binarySearch(arr, search_number);
if (result == -1) cout << "Number not present\n";
else cout << "Number is present at position " << (result + 1) << "\n";
} Совет: используйте стандартные алгоритмы (std::binary_search / std::lower_bound / std::upper_bound) в продукционных задачах — они проверены и оптимизированы.
Python: реализация и ввод
Python не имеет примитивных массивов, но списки (list) обеспечивают доступ по индексу. Ниже — итеративная функция и пример чтения с ввода:
def binarySearch(arr, arr_size, search_number):
low = 0
high = arr_size - 1
while low <= high:
mid = low + (high - low) // 2 # floor division
if arr[mid] == search_number:
return mid
elif arr[mid] > search_number:
high = mid - 1
else:
low = mid + 1
return -1
if __name__ == '__main__':
arr_size = int(input("Enter number of elements: "))
arr = []
print("Please enter numbers: ")
for _ in range(arr_size):
arr.append(int(input()))
search_number = int(input("Enter number to be searched: "))
result = binarySearch(arr, arr_size, search_number)
if result == -1:
print("Number not present")
else:
print("Number is present at position", result + 1)Примечание: в реальных задачах для пакетного ввода удобнее читать строку с числами и парсить через split(), особенно при больших объёмах данных.
Примеры вывода
Когда бинарный поиск не применим (контрпримеры)
- Массив не отсортирован — бинарный поиск даст неверный результат.
- Динамические структуры без произвольного доступа по индексу (например, связный список) — доступ к среднему элементу не O(1), поэтому выгода теряется.
- Очень маленькие массивы — для n малых константных значений линейный поиск может быть проще и быстрее в практике.
- Если нужно найти все вхождения значения, стандартный бинарный поиск вернёт какое‑то одно вхождение; для первого/последнего требуется вариация поиска границ.
Альтернативные подходы
- Интерполяционный поиск — эффективен на равномерно распределённых данных, в среднем быстрее, но в худшем — O(n).
- Прыжковый поиск (jump search) — полезен при упорядоченных массивах и медленном доступе к памяти.
- Хэш-таблицы — для поиска наличия элемента за O(1) амортизированно, но требуют дополнительной памяти и не дают информацию о позиции в исходном массиве.
Частые ошибки и ошибки новичков
- Неправильный расчёт mid как (low + high) / 2 без защиты от переполнения при больших индексах.
- Забвение проверки граничных условий (low <= high).
- Ожидание, что бинарный поиск найдёт «первое» вхождение при наличии дубликатов.
- Использование бинарного поиска на неотсортированных данных.
Тест-кейсы и критерии приёмки
Критерии приёмки (минимум):
- Функция возвращает индекс найденного элемента или -1 при отсутствии.
- Работает корректно на краевых случаях: пустой массив, массив из 1 элемента, элемент в начале/в конце, все элементы равны.
- Не вызывает переполнения при больших размерах массива.
Примеры тестов:
- Пустой массив: [] -> любое искомое значение -> -1
- Один элемент: [5], искать 5 -> 0
- Элемент в начале: [1,2,3], искать 1 -> 0
- Элемент в конце: [1,2,3], искать 3 -> 2
- Отсутствие: [1,3,5,7], искать 4 -> -1
- Дубликаты: [1,2,2,2,3], искать 2 -> вернуть индекс в диапазоне [1,3] (или модифицировать для первого/последнего)
Чек-лист по ролям
Интервьюер:
- Убедиться, что кандидат учитывает границы и переполнение.
- Попросить объяснить поведение при дубликатах.
Кандидат (студент/соискатель):
- Продемонстрировать итеративную реализацию с безопасным mid.
- Обсудить временную и пространственную сложность.
Инженер (производственный код):
- Рассмотреть использование std::lower_bound / std::upper_bound (C++).
- Добавить валидацию входных данных и обработку ошибок ввода.
Советы по отладке и производительности
- Логируйте low/high/mid при отладке на небольших массивах, чтобы увидеть, как сужается диапазон.
- Для больших массивов используйте профилирование, чтобы проверить, не является ли узким местом ввод/вывод, а не сам поиск.
- В критичных по памяти системах отдавайте предпочтение итеративной версии.
Факты и числа
- Время работы: O(log n) в среднем и в худшем случае для упорядоченного массива.
- Пространство: O(1) для итеративной реализации, O(log n) стек вызовов для рекурсивной.
- Поддержка больших n: бережно рассчитывайте mid, чтобы избежать переполнения.
Модификации для особых задач
- Первое вхождение: при совпадении смещайте правую границу, но сохраняйте позицию и продолжайте искать в левой части.
- Последнее вхождение: аналогично, но двигайте левую границу при совпадении.
- Поиск на убывающем массиве: меняйте направления сравнений (arr[mid] < search_number => high = mid - 1).
Пример изменённого поиска первого вхождения (Python)
def first_occurrence(arr):
low, high = 0, len(arr) - 1
result = -1
while low <= high:
mid = low + (high - low) // 2
if arr[mid] == target:
result = mid
high = mid - 1 # ищем дальше в левой части
elif arr[mid] > target:
high = mid - 1
else:
low = mid + 1
return resultБезопасность и приватность
Алгоритм сам по себе не обрабатывает конфиденциальные данные. В системах, где ввод поступает извне, проверяйте и фильтруйте ввод, чтобы предотвратить сбои из-за некорректных данных или переполнений.
Резюме
Итеративный бинарный поиск — это эффективный и экономный по памяти алгоритм для поиска в отсортированных массивах. Он обязателен к изучению для интервью и полезен в реальной практике. Используйте безопасный расчёт mid, учитывайте дубликаты и ограничивающие условия, выбирайте рекурсивный или итеративный вариант с учётом доступной памяти.
Важно:
- Всегда предполагаем, что вход отсортирован.
- Для производственного кода предпочитайте проверенные библиотеки, если они доступны.
Краткие ключевые выводы:
- Бинарный поиск: O(log n) по времени.
- Итеративная версия: O(1) по памяти.
- Требуется отсортированный массив; для дубликатов нужны модификации.
Спасибо за чтение! Надеемся, примеры кода и чек-листы помогут вам быстро реализовать и протестировать бинарный поиск в вашей среде.
Похожие материалы
Как пользоваться поисковыми операторами Google
Как изменить поисковик в Chrome
Лучшие Android-приложения для видеочата
Как пользоваться флеш‑накопителем в Windows 10
Доступ к Unstoppable Domains в браузере