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

Реализация двоичного поиска в Java

Если вы видели мою статью о сортировке по выбору, это означает, что вы уже знаете по крайней мере один алгоритм сортировки… С тегом java, алгоритмы, программирование, учебное пособие.

Если вы видели мою статью о Сортировка по выбору это означает, что вы уже знаете по крайней мере один алгоритм сортировки. Теперь, когда у вас есть отсортированный список (или вы можете его создать), мы собираемся погрузиться в алгоритм, который может искать элемент в этом отсортированном списке, это будет алгоритм двоичного поиска . Мы поговорим о двух подходах к этому алгоритму, итеративном и рекурсивный и об этом временная сложность тоже.

Как я делал во всех моих статьях о реализации A & DS с java до сих пор, мы собираемся создать два файла.

Итеративный Поиск

Давайте начнем создавать BinarySearch.java файл.

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

    void iterativeSearch(int[] arr, int key) {


        //just a placeholder
        int index = 0;

        //defines the first one in the array
        int first = 0;
        //defines last one in the array
        int last = arr.length - 1;
        //defines the mid point of this array
        int mid = (first + last)/2;
    }

Первое, что мы делаем, это создаем два параметра для нашего метода, массив и ключ (или значение), который мы хотим искать внутри этого массива, мы создаем переменные, которые мы будем использовать, index будет переменной, которая хранит позицию значения, которое мы ищем (индекс ключа). Переменная first – это индекс для первого значения в массиве (которое равно 0, потому что каждый массив начинается с 0). last – это индекс последнего значения в массиве, который мы можем получить, вычитая единицу из размера нашего массива. И, наконец, переменная mib , которая хранит среднюю точку нашего массива.

ВАЖНО: Почему мы получаем среднюю точку массива, используя сумму первого с последним, разделенную на два, вместо того, чтобы просто получить last/2 ? Нам нужно составить сумму с первым, потому что, когда мы начнем итерацию, первая позиция массива начнет меняться, увеличиваясь каждый раз, когда цикл завершается, что также приводит к изменению средней точки массива.

Теперь, когда у нас есть все наши переменные, мы собираемся создать логику для поиска ключа

    //while the array is not the size of zero
    while(first <= last) {
        if (arr[mid] < key) {
            //shifts to the right since is greater and we already checked mid
            first = mid + 1;
        }
        //if item in mid make it equal index, break the loop and return at the end
        else if (arr[mid] == key) {
            //define index as mid
            index = mid;
            //print result
            System.out.println("The position of " + key + " is " + index);
            //break from the while loop
            break;
        }
        else {
            //shifts the end point to the left of mid
            //since the value is smaller and we already checked the mid
            last = mid - 1;
        }
        // make new count now with the new value to first or last
        mid = (first + last)/2;
    }

Хорошо, здесь у нас есть базовый цикл while, который повторяется до тех пор, пока переменная first (в котором хранится начало массива) больше, чем last , что означает, что мы будем выполнять итерацию до конца списка. Внутри цикла мы начинаем создавать ваши условия, чтобы найти наш элемент. Способ двоичного поиска заключается в делении списка пополам до тех пор, пока он не найдет нужное число (вот почему мы создаем переменную mib ), и здесь играют наши условия. в первом операторе if мы делаем так, что если число в середине меньше , чем ключ (элемент, который мы ищем), мы обновим переменную first , чтобы она была позицией после средней позиции, что означает, что теперь мы будем искать только в правой половине списка, потому что все слева меньше нашего ключа, поэтому ключа там не будет.

Примеры изображений (взяты из tutorialspoint )

В этом примере 31 – это ключ

После того, как мы определили наш ключ, мы определяем среднюю точку списка (в данном случае 27).

Затем мы входим в наш цикл и выполняем наше первое условие, сравнивая, если среднее значение меньше ключевого

Условие выполнено, поэтому мы определяем, что первым будет элемент, оставленный для mod (в данном случае сам 31 или позиция 5), и мы повторяем цикл

Следующее условие – если значение средней точки равно ключу, если это элемент, который был найден, и мы можем прервать цикл, здесь мы используем переменную index просто для большей наглядности в print .

И последнее, но не менее важное: мы создаем наше окончательное условие, которое действительно похоже на наше первое условие, в этом случае (хотя и не указано явно в инструкции) проверяет, больше ли среднее значение, чем ключ, если это так, мы будем искать только в левой половине списка, определяя last как значение перед средней точкой.

Последнее, что у нас есть в нашем цикле while , который выполняется в каждом цикле, – это mid = (first + last)/2; , это необходимо, потому что каждый раз, когда выполняется цикл, мы проводим новые сравнения с mid и обновляем то, что мы ищем в списке, и если мы обновляем первую переменную и последнюю переменную, но не обновляем нашу mid переменную, мы будем продолжать с одной и той же средней точкой каждый раз, поэтому цикл будет выполнять одни и те же сравнения каждый раз.

if (first > last) {
            System.out.println("Not found");
        }
    }

Последнее, что есть в методе out итеративный поиск , – это просто простое условие, чтобы убедиться, что, если цикл заканчивается и ключ не был найден, ключа нет в списке, поэтому мы печатаем “не найдено”.

Хорошо, теперь, когда мы создали наш метод, давайте создадим наш BinarySearchMain.java чтобы запустить его!

public class BinarySearchMain {
    public static void main(String args[]) {
        BinarySearch binary = new BinarySearch();
        int[] arr = {10, 6, 4, 8, 2};
        int key = 8;

        binary.iterativeSearch(arr,key);
    }
}

Здесь мы просто создаем метод main и вызываем наш итеративный поиск , передавая определенный нами массив и ключ, теперь все работает! Просто попробуйте запустить его и поиграйте с разными массивами.

В приведенном выше случае результат должен быть таким

The position of 8 is 3

Временная сложность

Временная сложность итеративного поиска равна O (log n), потому что в худшем случае (если искомое число находится в конце списка или отсутствует в списке) нам придется многократно делить список пополам, пока мы не дойдем до конца списка. Наилучшим сценарием было бы O (1) (или Ω(1)), и это было бы в том случае, если элемент, который мы ищем, уже находится в средней точке массива.

Рекурсивный Поиск

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

    int recursiveSearch(int[] arr, int key, int first, int last) {

        //just a placeholder
        int index = 0;

        int mid = (first + last)/2;

        //base case
        if (first > last) {
            return -1;
        }

        if(arr[mid] < key) {
            //return recursive but with first being shifted right from              
            //mid getting only the right half of the array 
            return recursiveSearch(arr,key,mid + 1, last);
        }
        else if (arr[mid] == key) {
            //recursive is gonna end up here at the end
            index = mid;
            //return the value
            return index;
        }
        else {
            //return recursive but with last being shifted left from mid
            //getting only the left half of the array
            return recursiveSearch(arr,key,first, mid - 1);
        }
    }

Рекурсивный поиск довольно прост, мы начинаем с создания нашего метода recursive Search . Разница в том, что на этот раз вместо того, чтобы делать его пустым, мы делаем его как int , так что мы можем использовать возвраты для выполнения нашей рекурсии. Другое отличие метода заключается в том, что мы добавляем еще два параметра: int first и int last .

Внутри метода у нас есть базовый вариант, который находится в верхней части кода, который, если наш первый выше, чем в прошлом году, элемент отсутствует в этом списке. После этого у нас есть наш набор условий, аналогичный тому, что мы сделали в итеративном поиске . Но внутри этих операторов if у нас есть наши операторы return. Вот где мы собираемся использовать силу рекурсии, мы снова вызываем тот же рекурсивный поиск в нашем первом операторе if и передаем тот же массив, ключ и last. Но мы передаем наш первый как mid + 1 , заставляя его сдвигать начало массива только на правую половину массива.

В нашем следующем if мы делаем в основном то же самое, чтобы проверить, находится ли наш ключ в середине.

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

И мы закончили с рекурсивным поиском , теперь нам нужно только добавить в main, чтобы запустить его.

        int first = 0;
        int last = arr.length - 1;


        int recursive = binary.recursiveSearch(arr, key, first, last);

        if(recursive == -1) {
            System.out.println("Not found");
        }
        else {
            System.out.println("Found at position " + recursive);
        }

если он возвращает значение -1, как мы определили в нашем базовом случае, мы выводим “Не найдено”. в противном случае, мы нашли позицию, поэтому мы просто печатаем ее!

и это результат (использовался тот же тест, который я использовал для итеративного поиска)

Found at position 3

Временная сложность

Может быть, вы поняли в этом что-то особенное но… Временная сложность рекурсивного поиска равна O(log n) нашего итеративного поиска ! В худшем случае мы все равно будем делить список пополам несколько раз. И в лучшем случае сценарий по-прежнему O(1) или ( Ω(1) )

Итак, вы, возможно, спрашиваете себя: “В чем разница?” Честно говоря, единственная разница заключается в Сложности пространства но это уже кое-какой контент для целой другой статьи. Итак, в конце концов, это вопрос выбора, какой из них вам легче понять? Иди и используй это!

Если вы хотите увидеть полный код, вы можете увидеть его здесь, в этом репозитории github/|

Этот пост я начал писать в конце 2020 года, но вернулся к нему только в 2021 году. Из-за этого я чувствовал, что это недостаточно хорошо, и не был уверен, стоит ли мне публиковать это или нет. Я решил опубликовать, потому что, если это действительно плохо, худшее, что может случиться, – это не быть кому-то полезным, если это не так уж плохо, это все равно может кому-то помочь. Но просто позволять оставаться внутри моего компьютера без всякой причины – это даже не шанс кому-то помочь.

Если у вас есть какие-либо предложения или критические замечания по поводу того, что я должен написать или/и как я должен написать (более подробно? менее подробно?) или даже как моя грамматика (не носитель английского языка), не стесняйтесь писать об этом в комментариях, это мне очень помогло бы!

Связи

Оригинал: “https://dev.to/gmkonan/implementing-binary-search-in-java-489a”