Бинарный поиск в C++: подробное руководство
Сегодня мы и изучим его. Также попытаемся ответить на такие вопросы: как бинарный поиск работает в программе, плюсы и минусы его использования у себя в коде и в каких структурах данных можно его применять, а в каких нет. Поехали!
Что такое бинарный поиск
Линейный поиск по сравнению с бинарным — дешевая подделка. Бинарный поиск — очень быстрый алгоритм с не сложной реализацией, который находит элемент с определенным значением в уже отсортированном массиве.
Очень важно помнить! Алгоритм будет работать правильно, только с отсортированным массивом. А если по случайности вы забыли отсортировать массив перед его использованием, то в большинстве случаев тот ответ, который подсчитал алгоритм, будет неверным.
Принцип работы бинарного поиска
Давайте рассмотрим задачку, чтобы понять как работает алгоритм.
Охранной службе морского флота поступило сообщение о том, что некто пытается пронести на корабль бомбу. Диверсант пока находится у причала. Допустим, что в нашем распоряжении имеется прибор, который может определить, есть ли бомба в комнате. Необходимо как можно быстрее определить, у кого находится бомба.
Самым легким и самым долгим по времени решением, будет поочередная проверка пассажиров в комнате с прибором (это линейный поиск). Но это слишком долго.
Давайте сделаем по-другому. Поделим пополам всех пассажиров и разведем их по двум комнатам. Так с помощью прибора мы сможем узнать, в какой из двух комнат находится бомба. В итоге такого маневра мы уменьшим число подозреваемых в два раза. С остальным числом подозреваемых сделаем также: разделим их на две группы и разместим в разных комнатах. Так продолжаем, пока не узнаем кто диверсант.
В задаче выше охранники использовали алгоритм двоичного поиска для нахождения диверсанта. В обычной программе принцип работы бинарного поиска такой же, как и в примере, выше.
Как создать бинарный поиск в C++
Давайте посмотрим как работает бинарный поиск на примере.В примере ниже в строке 9 мы создали массив arr
на 10 элементов и в строке 12 предложили пользователю с клавиатуры заполнить его ячейки.
В строке 20 мы предлагаем пользователю ввести ключ (который нужно будет найти в массиве), а дальше мы с бинарным поиском проверим массив на наличие введенного ключа пользователем. Если мы найдем ключ в массиве, то выведем индекс ячейки, в которой находится ключ.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include <iostream> #include <algorithm> using namespace std; int main() { setlocale(LC_ALL, "rus"); int arr[10]; // создали массив на 10 элементов int key; // создали переменную в которой будет находиться ключ cout << "Введите 10 чисел для заполнения массива: " << endl; for (int i = 0; i < 10; i++) { cin >> arr[i]; // считываем элементы массива } sort (arr, arr + 10); // сортируем с помощью функции sort (быстрая сортировка) cout << endl << "Введите ключ: "; cin >> key; // считываем ключ bool flag = false; int l = 0; // левая граница int r = 9; // правая граница int mid; while ((l <= r) && (flag != true)) { mid = (l + r) / 2; // считываем срединный индекс отрезка [l,r] if (arr[mid] == key) flag = true; //проверяем ключ со серединным элементом if (arr[mid] > key) r = mid - 1; // проверяем, какую часть нужно отбросить else l = mid + 1; } if (flag) cout << "Индекс элемента " << key << " в массиве равен: " << mid; else cout << "Извините, но такого элемента в массиве нет"; system("pause"); return 0; } |
Бинарный поиск находится в строках 29 — 35. В строке 27 мы создали переменную mid
, в которой будет храниться индекс среднего элемента (из отрезка [l
, r
]). В строке 27 считываем средний элемент отрезка [l
, r
] в переменную mid,
по формуле: (l
+ r
) / 2 (в которой l
— левая граница, r
— правая граница). В строке 29 проверяем условие arr[mid] == key
:
- Если результат условия равен
true
, то булевой переменнойflag
присваиваем значениеtrue
(это значит, что мы нашли ключ).
В строке 30 мы проверяем условие arr[mid] > key
:
- Если значение
arr[mid]
больше ключа, то переменнойr
присваиваем значениеmid
. Потому что проверять верхнюю часть не имеет смысла, так как ключ может находится только в ячейках ниже индексаmid
(это если массив отсортирован по возрастанию). - Если значение
arr[mid]
меньше ключа, то переменнойl
присваиваем значениеmid
. Потому что проверять нижнюю часть не имеет смысла, так как ключ может находится только в ячейках выше индексаmid
(это если массив отсортирован по возрастанию).
В строке 37 мы проверяем flag
:
- Если
flag
равенtrue
, значит мы нашли ключ, а значит выводим «Индекс элементаkey
в массиве равенmid
«. - Если
flag
равенfalse
, то выводим «Извините, но такого элемента в массиве нет».
Давайте выполним этот код:
А если такого ключа нет в массиве :
Как можно улучшить бинарный поиск
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
int l = 0; int r = n; // в этом случае присваивается именно n int mid; while (l < r) { mid = (l + r) / 2; // считываем срединный индекс отрезка [l,r] if (arr[mid] > key) r = mid; // проверяем, какую часть нужно отбросить с поиска else l = mid + 1; } r--; // уменьшаем на один if (arr[r] == key) cout << "Индекс элемента " << key << " в массиве равен: " << r; else cout << "Извините, но такого элемента в массиве нет"; |
В этом случае изначально r
нам нужно присвоить значение n
, а не n - 1
как обычно. После выполнения, в переменной r - 1
будет храниться номер элемента, равного key
, если такой элемент конечно есть в массиве. В ином случае, если элемента нет, то r
будет место в массиве, куда можно вставить переменную key
и не нарушить упорядоченность массива (такой принцип используются в сортировке бинарными вставками).
В строке 12 заранее уменьшаем r на один.
Плюсы и минусы использования бинарного поиска
Плюсы:
- Реализация алгоритма достаточна легкая;
- Быстрая работа алгоритма;
Минусы:
- Для поиска массив должен быть упорядочен(отсортирован);
- Двоичный поиск должен иметь возможность обратится к любому элементу данных (по индексу). А это значит, что все структуры данных, которые построены на связных списках использоваться не могут;
Мы советуем вам всегда использовать бинарный поиск при работе с массивами или векторами. Обычно прогеры применяют бинарный поиск с помощью функций.
Закрепление бинарного поиска
Для закрепления материала объявите массив на 10 элементов, предложите пользователю заполнить массив и ввести ключ. Используя бинарный поиск найдите ключ в массиве.
Надеемся, что эта статья помогла вам узнать, что такое бинарный поиск и как он работает. Если вы нашли ошибки в статье или хотите задать вопрос, то пишите в комментарии. Удачи!
Оно не правильно выводит индекс массива
Всё правильно выводиться. Индексация массивов начинается с 0, а не с 1! Если вам это ломает мозг,то при в cout-e напишите mid+1
Полностью согласен
Оно не правильно выводит индекс массива
Сужаем массив до 3 элементов
Вводим 5, 4, 3 ключ 5
получаем
Индекс элемента 5 равен 2
Код неверный! Индекс выводит не правильно!
Спасибо за отличный материал.
А что будет если в массиве будут повторяющиеся элементы? Индекс которого будем возвращать?
Вероятнее всего индекс элемента,который будет найден первее.
Будет выведен последний из повторяющихся элементов. Например, если мы ищем значение 2 для такого массива [1, 1, 1, 1, 2, 2, 2, 3, 3, 3], будет выведен индекс 6.
Улучшенный поиск не находит последний элемент, т.к. r=8 при длине массива 10. Т.е. выдает предпоследний элемент списка.
Спасибо, исправили!
Указано, что в строке 8 уменьшаем r на 1. На самом деле это в строке 12.
Спасибо, исправил.