- Binary search in java algorithm
- Comparison with other Searching
- Some problems on Binary Search
- Variants of Binary Search
- Comparison with other Searching
- Some problems on Binary Search
- Binary search in java algorithm
- Binary Search vs. Linear Search
- Runtime of Binary Search for Small Arrays
- Runtime of Binary Search in a LinkedList
- When Is Binary Search in a LinkedList Useful?
- Summary
- Бинарный поиск на Java
- Без рекурсии
- С использованием рекурсии
- Binary search algorithm in Java
- 2. How to implement binary search
- 2.1 Find the index of the middle position
- 2.2 Eliminate and continue
- 3. Implementing Binary Search
- 3.1 Binary Search using while loop
- 3.2 Binary Search using Recursion
- 4. Binary search efficiency
- Conclusion
Binary search in java algorithm
Comparison with other Searching
Some problems on Binary Search
- Check if an array is sorted and rotated using Binary Search
- Longest Common Prefix using Binary Search
- Find the Peak Element in a 2D Array/Matrix
- Search an element in a sorted and rotated array with duplicates
- Search for an element in a Mountain Array
- Median of two Sorted Arrays of Different Sizes
- Longest Increasing Subsequence Size (N log N)
- Median of two Sorted Arrays of Different Sizes using Binary Search
- The Painter’s Partition Problem using Binary Search
- Allocate Minimum Number of Pages from N books to M students
- Find largest median of a sub array with length at least K
Variants of Binary Search
Comparison with other Searching
Some problems on Binary Search
- Check if an array is sorted and rotated using Binary Search
- Longest Common Prefix using Binary Search
- Find the Peak Element in a 2D Array/Matrix
- Search an element in a sorted and rotated array with duplicates
- Search for an element in a Mountain Array
- Median of two Sorted Arrays of Different Sizes
- Longest Increasing Subsequence Size (N log N)
- Median of two Sorted Arrays of Different Sizes using Binary Search
- The Painter’s Partition Problem using Binary Search
- Allocate Minimum Number of Pages from N books to M students
- Find largest median of a sub array with length at least K
Binary search in java algorithm
We can verify the theoretically derived time complexity with the program BinarySearchRuntime from the GitHub repository. The program generates random arrays with 10,000 to 200,000,000 elements and searches them for a randomly selected element.
Since the times are in the nanosecond range, each measurement consists of searches for 100 different keys. The measurement is repeated 100 times for each array size; then, the median is printed. The following graph shows the average runtime in relation to the array size:
The logarithmic progression can be seen very well.
Binary Search vs. Linear Search
With linear search, the best case is finding the element we are looking for in the first step. In the worst case, we have to search the entire array. In the average case, half of the entries. With n entries, that is n/2 search steps. The duration of the search increases linearly with the number of entries. We say:
The time complexity of the linear search is O(n).
We can measure the runtime of linear search with the LinearSearchRuntime program. The following image shows the comparison of the runtimes of binary and linear search. I had to reduce the range to 100,000 elements to be able to recognize at least a minimal increase of the measured values for the binary search:
We can see the linear time of the linear search very nicely. It is also apparent that the binary search is orders of magnitude faster than the linear search.
Runtime of Binary Search for Small Arrays
Due to the higher complexity of the binary search code, linear search can be faster for small arrays. The following diagram shows a section of the comparison of run times for up to 500 elements. Each measurement point is the median of 100 measurements with 10,000 repetitions each.
That confirms the assumption. For arrays up to a maximum of about 230 elements, linear search is faster than binary search. Of course, this is not a general statement but applies only to my laptop and the JDK I currently use.
You can once again nicely see the linear time – O(n) – compared to the logarithmic time – O(log n).
Runtime of Binary Search in a LinkedList
In the chapter Binary Search in the JDK, I mentioned that the Collections.binarySearch() method can also be applied to a LinkedList . Collections.binarySearch() distinguishes internally between lists that implement the RandomAccess interface, such as ArrayList , and other lists. For lists with «random access», a regular binary search is performed.
To access the middle element in lists without random access, we would have to follow the elements from the beginning to the middle, element by element. From there, we would again reach the center of the left or right half by following the list, element by element. The following diagram should illustrate this:
For example, to find the position of 19, we would first have to follow the orange arrows to the center, then the blue arrows back to 23, and finally the yellow arrow to 19.
That works only with a doubly linked list. For iterating left in a singly linked list, you would have to jump back to the beginning and, from there, follow the arrows to the right again.
No matter if singly or doubly linked – in any case, we have to iterate over more elements than with linear search. While we have an average of n/2 search steps in the linear search in total, we already iterate over n/2 elements to reach the middle in the first step of the binary search. In the second step, we iterate over n/4 elements; in the third step, we iterate over n/8 elements, and so on.
So at first glance, binary search makes absolutely no sense in a LinkedList .
When Is Binary Search in a LinkedList Useful?
Nevertheless, binary search in a LinkedList can be faster than linear search. Although we have to iterate over more elements (as shown in the previous section) – the number of comparisons remains in the order of O(log n)!
Depending on the cost of the comparison function – which can be significantly higher for an object than for a primitive data type – this can make a considerable difference. So if you ever need to search in a LinkedList , it’s worth trying binary search with Collections.binarySearch() and comparing it to linear search.
Summary
This article has shown the principle of binary search and its advantages over linear search for sorted arrays and lists. I demonstrated the theoretically derived time complexity on an example. I also showed that binary search could be useful for a doubly linked list.
A very similar technique is the search in a binary search tree.
If you liked the article, feel free to leave me a comment or share it using one of the share buttons at the end. If you want to be informed when the next articles are published, sign up for my newsletter using the form below.
Бинарный поиск на Java
С одной стороны, для подобных алгоритмов используют уже готовые функции стандартной библиотеки, с другой – подобные вопросы на собеседованиях позволяют узнать полезное о кандидате.
Первое что приходи на ум: перебор элементов в массиве до нужного, тогда если количество элементов равно n и нужный нам элемент будет последним, нам потребуется сделать n проверок элементов до нахождения нужного, про такой случай и говорят что сложность алгоритма равна O(n).
Рассмотрим другой подход — бинарный поиск – возьмем средний элемент отсортированного массива и сравним его c искомым. Если элемент меньше – продолжим поиск в левой части массива, если больше в правой, пока не останется нужный элемент. Таким образом нам понадобится число операций равное тому, сколько раз нам нужно поделить массив размером n пополам.
Например, для массива в 16 элементов мы сначала поделим его на два по 8, потом 8 на два по 4, потом 4 на два по 2 и на конец 2 пополам, те всего 4 операции в худшем случае. Такое число равно двоичному логарифму.
Без рекурсии
public class Binary < public static void main(String[] args) < int[] values = ; int valueToFind = 3; System.out.printf("Index = %d%n", binarySearch(values, valueToFind, 0, values.length - 1)); > private static int binarySearch(int[] sortedArray, int valueToFind, int low, int high) < int index = -1; while (low else if (sortedArray[mid] > valueToFind) < high = mid - 1; >else if (sortedArray[mid] == valueToFind) < index = mid; break; >> return index; > >
С использованием рекурсии
public class Binary < public static void main(String[] args) < int[] values = ; int valueToFind = 3; System.out.printf("Index = %d%n", binarySearch(values, valueToFind, 0, values.length - 1)); > private static int binarySearch(int[] values, int valueToFind, int l, int r) < if (l == r) < return (values[l] == valueToFind) ? l : -1; >int m = l + (r - l) / 2; if (valueToFind > values[m]) < return binarySearch(values, valueToFind, m + 1, r); >else if (values[m] > valueToFind) < return binarySearch(values, valueToFind, l, m - 1); >return m; > >
Если элемент не найден, то вернется -1 .
Во многих примерах в интернете можно встретить запись int m = (l + r) / 2; , вместо int mid = l + (r — l) / 2; . И у меня в заметке тоже была такая запись, пока один из читателей не обратил на это внимание.
Но использование второго варианта является лучшей практикой, так как это помогает избежать переполнения, когда размер массива велик.
Например, если l = 2147483647 и r = 2147483647 , сумма l и r будет равна 4294967294, что превышает максимальное значение, которое может удерживать int , вызывая переполнение.
С другой стороны, если вы используете mid = l + (r — l) / 2; это будет работать, как и ожидалось, потому что вычитание будет сделано первым, а результат будет равен нулю, поэтому деление будет равно нулю, а сложение вернет значение l .
Binary search algorithm in Java
Binary search is used to find a target value in an array. The goal is to find the target value with a minimum number of attempts or iterations. The binary search algorithm operates on sorted arrays. Search iterations are performed on the array by repeatedly dividing the search interval in half. If the search value is less than the value in the middle of the search interval, discard the upper half and continue with the lower half. Iterate this process until the search value is found or no interval is left.
2. How to implement binary search
Let’s take the below array and implement the binary search algorithm to find a given value. To make it practical, let’s begin with an unsorted array.
The first step is to sort the array to make it binary sort ready. This is crucial since we use the indexes of the array elements during the sorting process to compare the actual value contained in the index. The below code uses java to sort the array, but the logic is applicable for any programming language.
The sorted array will look like
Now, this array is ready to be binary searched. Let’s pick a random number to search: 40. At the end of this tutorial, we will do the full code implementation of binary search. With our implementation, a search for random value 40 would look like this.
2.1 Find the index of the middle position
Our sorted array length is 10. Since we use the index of the position, the first index becomes 0, and the last index is array length -1. Using these numbers, we can calculate the middle position as:
start = 0
end = array.length-1 = 9
middle = ((start + end) / 2) = int 9 / int 2 = 4
This leads us to position 4 in the array. Position 4 holds the value 6, which is smaller than the value we are looking for. Now we know 40 lies between index 5 and 9. Therefore, we can discard the portion of the array lower than index 5 (lower half) and continue our search with the upper half where the index is 5 or greater.
2.2 Eliminate and continue
Now our focus is between index 5 and 9. Let’s eliminate the remaining portion of the array. Our start position becomes middle + 1 and the end position remains the end of the array.
Since 40 > 6 at array[middle]
start = middle + 1 = 5
middle = (5 + 9) / 2 = 7
Index 7 is our new middle. This position holds 31, which is still smaller than our search value of 40.
Now we can eliminate the array index lower than 8. Our search value lies on or above index position 8.
Since 40 > 31 at array[middle]
start = middle + 1 = 8
middle = (8 + 9) / 2 = 8
Now our middle is 8 and we find what we are looking for. At this point, it is not necessary to continue with the search, we can simply return the position and exit the function.
The source code for this example is listed below.
3. Implementing Binary Search
3.1 Binary Search using while loop
This is the preferred implementation for larger arrays.
private static int binarySearch(int[] array, int val) < Arrays.sort(array); int start = 0; int end = array.length-1; int middle; while(start else < end = middle -1; >> return -1; >
3.2 Binary Search using Recursion
Recursion consumes more resources. Every recursive iteration has to maintain its stack. Therefore, this method could be slower for larger arrays.
static int binarySearch(int[] arr, int key, int start, int end) < if(start >end) < return -1; >int middle = (start+end)/2; if(arr[middle] == key) < return middle; >if(arr[middle] > key) < return binarySearch(arr, key, start, middle-1); >else < return binarySearch(arr, key, middle+1, end); >>
4. Binary search efficiency
Binary Search time complexity is O(log(n)). In the best-case scenario, binary search has the efficiency of O(1) and O(log (n)) in the worst-case scenario.
For an array of 8 elements:
In every iteration we devide the array by 2, therefore log2 of 8
O(log (n)) => O(log2 (8)) => 3
Maximum of 3 iterations to find the key
For an array of 18 elements:
O(log (n)) => O(log2 (16)) => 4
Maximum of 4 iterations to find the key
Conclusion
Binary search works only on sorted arrays. Binary search operates by dividing the array into half in every iteration, therefore called Binary Search. In every division, the algorithm narrows down the search context and discards the array portion that does not contain the search key.