Рубрики
Без рубрики

Алгоритм бинарного поиска в Java

Узнайте, как и когда использовать алгоритм бинарного поиска.

Автор оригинала: baeldung.

1. Обзор

В этой статье мы рассмотрим преимущества бинарного поиска по сравнению с простым линейным поиском и рассмотрим его реализацию в Java.

2. Необходимость эффективного поиска

Допустим, мы занимаемся продажей вина, и миллионы покупателей ежедневно посещают наше приложение.

Через наше приложение клиент может отфильтровать товары, цена которых ниже n долларов, выбрать бутылку из результатов поиска и добавить ее в свою корзину. У нас есть миллионы пользователей, которые каждую секунду ищут вина с ограничением цены. Результаты должны быть быстрыми.

На бэкэнде наш алгоритм выполняет линейный поиск по всему списку вин, сравнивая предельную цену, введенную клиентом, с ценой каждой бутылки вина в списке.

Затем он возвращает товары, цена которых меньше или равна предельной цене. Этот линейный поиск имеет временную сложность O(n) .

Это означает, что чем больше количество винных бутылок в нашей системе, тем больше времени это займет. Время поиска увеличивается пропорционально количеству введенных новых элементов.

Если мы начнем сохранять элементы в отсортированном порядке и искать элементы с помощью двоичного поиска, мы сможем достичь сложности O(log n) .

При бинарном поиске время, затраченное на поиск результатов, естественно увеличивается с размером набора данных, но не пропорционально.

3. Бинарный поиск

Проще говоря, алгоритм сравнивает значение key со средним элементом массива; если они неравны, то половина, в которой ключ не может быть частью, исключается, и поиск продолжается для оставшейся половины до тех пор, пока он не будет успешным.

Помните – ключевым аспектом здесь является то, что массив уже отсортирован.

Если поиск заканчивается тем, что оставшаяся половина пуста, то ключа в массиве нет.

3.1. Итеративная Реализация

public int runBinarySearchIteratively(
  int[] sortedArray, int key, int low, int high) {
    int index = Integer.MAX_VALUE;
    
    while (low <= high) {
        int mid = (low + high) / 2;
        if (sortedArray[mid] < key) {
            low = mid + 1;
        } else if (sortedArray[mid] > key) {
            high = mid - 1;
        } else if (sortedArray[mid] == key) {
            index = mid;
            break;
        }
    }
    return index;
}

Метод run Binary Search Итеративно принимает в качестве аргументов sorted Array , key & the low & | high индексы sorted Array . При первом запуске метода low , первый индекс sortedArray, равен 0, а high , последний индекс sortedArray, равен его длине – 1.

middle – это средний индекс отсортированного массива . Теперь алгоритм запускает цикл while , сравнивающий ключ со значением массива среднего индекса отсортированного массива .

3.2. Рекурсивная Реализация

Теперь давайте также рассмотрим простую рекурсивную реализацию:

public int runBinarySearchRecursively(
  int[] sortedArray, int key, int low, int high) {
    int middle = (low + high) / 2;
        
    if (high < low) {
        return -1;
    }

    if (key == sortedArray[middle]) {
        return middle;
    } else if (key < sortedArray[middle]) {
        return runBinarySearchRecursively(
          sortedArray, key, low, middle - 1);
    } else {
        return runBinarySearchRecursively(
          sortedArray, key, middle + 1, high);
    }
}

Метод run Binary Search Recursively принимает sorted Array , key, the low and high индексы sorted Array .

3.3. Использование Arrays.BinarySearch()

int index = Arrays.binarySearch(sortedArray, key);

Сортированный массив и ключ int |, который должен быть найден в массиве целых чисел, передаются в качестве аргументов методу BinarySearch класса Java Arrays .

3.4. Использование Collections.BinarySearch()

int index = Collections.binarySearch(sortedList, key);

Сортированный список & an Integer | key , который должен быть найден в списке объектов Integer , передаются в качестве аргументов методу BinarySearch класса Java Collections .

3.5. Производительность

Использовать ли рекурсивный или итеративный подход для написания алгоритма-это в основном вопрос личных предпочтений. Но все же есть несколько моментов, о которых мы должны знать:

1. Рекурсия может быть медленнее из – за накладных расходов на поддержание стека и обычно занимает больше памяти
2. Рекурсия не является стеком –
дружественной. Это может вызвать StackOverflowException
при обработке больших наборов данных
3. Рекурсия добавляет ясности коду, так как делает его короче по сравнению с итеративным подходом

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

Следует знать, что этот анализ носит теоретический характер и может варьироваться в зависимости от контекста.

Кроме того, алгоритм бинарного поиска нуждается в отсортированном наборе данных, который также имеет свои издержки . Если мы используем алгоритм сортировки слиянием для сортировки данных, то в наш код добавляется дополнительная сложность n log n .

Поэтому сначала нам нужно хорошо проанализировать наши требования, а затем принять решение о том, какой алгоритм поиска лучше всего соответствует вашим требованиям.

4. Заключение

Этот учебник продемонстрировал реализацию алгоритма бинарного поиска и сценарий, в котором было бы предпочтительнее использовать его вместо линейного поиска.

Пожалуйста, найдите код для учебника на GitHub .