Целочисленный двоичный поиск
Пусть мы хотим найти в отсортированном массиве длины .
Применим концепцию из задачи "угадай число от до ".
Более формально. Используем идею метода "разделяй и властвуй" — посмотрим на элемент в середине массива . Если , то мы нашли . Если , то может находиться среди элементов правее — , так как все элементы левее -го меньше (массив отсортирован). Если , аналогичными рассуждением получаем, что ответ стоит искать в .
После первого вопроса мы или угадали или получили нужный нам отрезок для поиска. Представим, что этот отрезок новый массив . Теперь в отсортированном массиве надо найти элемент .
Получается алгоритм использует "разделяй и властвуй" (на задаче на отрезке переходит на более малый отрезок), на каждой итерации он сравнивает с центральным элементов на отрезке и в случае неудачи сокращает длину отрезка в два раза. Итерации алгоритма совершаются пока мы не найдём или длина отрезка станет равна .
Поскольку на каждом шаге длина отрезка уменьшается вдвое, это произойдёт через шагов.
Примерная реализация алгоритма двоичного поиска:
Функция binarySearch
ищет в массиве длины , возвращает индекс, по которому лежит , или , если в массиве нет.
int binarySearch(int* a, int n, int x) {
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2; // индекс элемента в центре отрезка [l, r]
if (a[mid] == x) {
return mid;
} else if (a[mid] < x) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return -1;
}
На самом деле проще писать бинпоиск лишь с одним условием внутри. Подробнее написано в следующей главе.
int binarySearch(int* a, int n, int x) {
int l = 0, r = n - 1;
while (r - l > 1) {
int mid = (l + r) / 2;
if (a[mid] < x) {
l = mid;
} else {
r = mid;
}
}
return a[r] == x || a[l] == x;
}
Проверка на a[l] = x
нужна лишь если a[0] = x
, так как в таком случае после поиска l
будет равна 0
.
Количество вхождений в массив
Дан массив и вопросов. Каждый запрос задан числом . Для каждого запроса определите количество вхождений в массив .
Решение : Отсортируем массив . Для каждого с помощью бинпоиска найдём количество вхождений.
Для количества вхождений в отсортированном массиве , достаточно получить индексы самого левого и самого правого вхождений в массив. Тогда количество вхождений — это разность этих индексов плюс один.
Сделаем два различных бинпоиска, для поиска l и r. Первый вернёт минимальное такое, что или если все элементы меньше . Второй найдёт максимальное такое, что или , если все элементы не больше . В данном случае ответ будет разница этих индексов. В случае, если нет в массиве то результат будет , так как . В обычном мире первая функция называется lower_bound
, а вторая lower_bound
. Подробнее почитать про них в языке c++
можно тут и тут, а тут про их различие.