Линейный и бинарный поиск
Линейный поиск работает на любых данных. Бинарный требует монотонного порядка и структуры с быстрым доступом по индексу.
Линейный поиск
static int linearSearch(int[] values, int target) {
for (int i = 0; i < values.length; i++) {
if (values[i] == target) return i;
}
return -1;
}Инвариант перед итерацией i: в диапазоне [0, i) target отсутствует. Время: best O(1), worst O(n), память O(1).
Бинарный поиск
Используем полуинтервал [left, right):
static int binarySearch(int[] sorted, int target) {
int left = 0;
int right = sorted.length;
while (left < right) {
int middle = left + (right - left) / 2;
int value = sorted[middle];
if (value < target) {
left = middle + 1;
} else if (value > target) {
right = middle;
} else {
return middle;
}
}
return -1;
}left + (right - left) / 2 избегает overflow выражения (left + right) / 2. Инвариант: если target существует, он находится внутри [left, right).
Первое вхождение
При дубликатах обычный поиск возвращает любое вхождение. Lower bound ищет первую позицию >= target:
static int lowerBound(int[] sorted, int target) {
int left = 0, right = sorted.length;
while (left < right) {
int middle = left + (right - left) / 2;
if (sorted[middle] < target) left = middle + 1;
else right = middle;
}
return left;
}Target найден, если left < length && sorted[left] == target.
Сортировать несортированный массив ради единственного поиска обычно невыгодно: O(n log n) + O(log n) хуже O(n). Для множества запросов подготовка может окупиться.
На собеседовании
- Главное предусловие binary search? Монотонный порядок по тому же comparator.
- Сложность?
O(log n)времени, итеративноO(1)памяти. - Как найти первое вхождение? Lower bound.
- Почему binary search плох для LinkedList? Доступ к середине
O(n). - Что вернуть для пустого массива?
-1, lower bound —0.