Correct way of Binary Search Algorithm - java

I just learned about the Binary Search algorithm and I tried to implement it. I have used a few test cases and it seems to work fine. However, when I checked on GeeksforGeeks, there are quite a few differences in the way they handled the indexing. Can anyone shed some lights on whether my implementation is good or not. And if not, how would it fail to work?
Here's my code:
static int binarySearch(int arr[], int i, int r, int x) {
if(r > 1) {
int middle = r/2;
if(arr[middle] == x) {
return middle;
}
if(arr[middle] > x) {
return binarySearch(arr, i, middle, x);
}else {
return binarySearch(arr, middle, r+1, x);
}
}
return -1;
}

Suppose that our function is going to do a binary search on the subarray arr[l..r] (inclusive). Here, r - l + 1 is the length of this subarray and when it's not a positive number (or r < l), we don't have a valid subarray and we can't search for anything, so the function stops the recursion and returns -1.
You know, we always make two subarrays with (almost) equal length. So, middle = r/2 is not correct and middle = (l + r) / 2 is correct. The new subarrays are arr[l..middle-1] and arr[middle+1..r]. We divide the subarray after checking its middle element, so these two subarrays should not contain the middle index as you see.
Now, we can rewrite your code.
static int binarySearch(int arr[], int l, int r, int x) {
if (r >= l) {
int middle = (l + r) / 2; //int middle = l + (r - l) / 2; for avoiding integer overflow
if(arr[middle] == x)
return middle;
if (arr[middle] > x)
return binarySearch(arr, l, middle - 1, x);
else
return binarySearch(arr, middle + 1, r, x);
}
return -1;
}
And this is a non-recursive version of binary search algorithm. Usually, it can work slightly faster.
static int binarySearch2(int arr[], int l, int r, int x) {
while (r >= l) {
int middle = (l + r) / 2; //int middle = l + (r - l) / 2; for avoiding integer overflow
if (arr[middle] == x)
return middle;
if (arr[middle] > x)
r = middle - 1;
else
l = middle + 1;
}
return -1;
}
Don't forget to sort your array before calling these functions.
Suppose that n is the length of the given array.
For 0-based arrays, the correct form of calling the functions is this:
binarySearch(arr, 0, n - 1, x);
and
binarySearch2(arr, 0, n - 1, x);
For 1-based arrays, the correct form of calling the functions is this:
binarySearch(arr, 1, n, x);
and
binarySearch2(arr, 1, n, x);

Related

Java binary search without specific method

I have to create an ordered array of numbers. Next, I should enter a number and find it with the binary search.
I have seen that there is a very simple method (java.util.Arrays.binarySearch(int[] a, int key)), but I can not use it.
How can I do a binary search without this method? Thank you for your support.
For example, I have this array: A = [1, 5, 10, 21, 30, 50]
And I have to find the number 30.
Pass your array and number to be searched in the following method
public int binarySearch(int arr[], int num)
{
int left = 0, right = arr.length - 1;
while (left <= right) {
int middle = left + (right - l) / 2;
if (arr[middle] == num)
return middle;
if (arr[middle] < num)
left = middle + 1;
else
right = middle - 1;
}
return -1;
}
Also good explanation can be found at https://www.geeksforgeeks.org/binary-search/

How to implement randomized O(n) algorithm for finding median of unsorted array in Java?

I have this code for finding median of an unsorted array in O(n) expected time, O(n^2) worst case. I use longs because I want to be able to hold long values.
public class Randomized {
long kthSmallestHelper(long arr[], long l, long r, long k)
{
if (k > 0 && k <= r - l + 1)
{
long pos = randomPartition(arr, l, r);
if (pos-l == k-1)
return arr[(int)pos];
if (pos - l > k - 1)
return kthSmallestHelper(arr, l, pos - 1, k);
return kthSmallestHelper(arr, pos + 1, r, k - pos + l - 1);
}
return Integer.MAX_VALUE;
}
void swap(long arr[], long i, long j)
{
long temp = arr[(int)i];
arr[(int)i] = arr[(int)j];
arr[(int)j] = temp;
}
long partition(long arr[], long l, long r)
{
long x = arr[(int)r], i = l;
for (long j = l; j <= r - 1; j++)
{
if (arr[(int)j] <= x)
{
swap(arr, i, j);
i++;
}
}
swap(arr, i, r);
return i;
}
long randomPartition(long arr[], long l, long r)
{
long n = r - l + 1;
long pivot = (long)(Math.random()) * (n - 1);
swap(arr, pivot + 1, r);
return partition(arr, l, r);
}
long kthSmallestRandom(long arr[], long k){
return kthSmallestHelper(arr, 0, arr.length - 1, k);
}
}
But when I run it
long[] array = {12, 3, 5, 7, 4, 19, 26}; //median is 7
Randomized rand = new Randomized();
System.out.println(rand.kthSmallestRandom(array, (array.length + 1)/2));
It's incorrect (it returns 4).
My idea was to use this version of the kth smallest number to say that I want the (length/2)th smallest, which is the median.
What is wrong with my idea or implementation?
There is a small error in your kthSmallestHelper function in this line:
if (pos-l == k-1)
return arr[(int)pos];
The return uses the wrong index. Try pos-l+1 instead of pos (and cast it to int). This returns the kth item, which was just sorted to its correct place in the array.
I'm not in a location to run it, but it seems like a lot could be wrong at this point.
In Java, when you're passing an array into a function only a copy of the array is passed, so any swaps you would do would not change the array outside of that method. Also looks like your partitions are off. Instead of passing in the partition you're just passing in the left and right bounds, and it looks like your partition method only ever sends things to the left of the partition.
This code should be commented a lot more so we can tell what you're trying to do at each point. ALso it looks like since you're swapping things into a sorted order it would be O(nlogn) since you can't sort in linear time. And why can't you use k == 0 for the kth smallest helper?
I don't know why, but just changing all longs to ints fixed it. It now works as expected.

Find the number of ways to represent n as a sum of two integers with boundaries

I am playing around codefight, but I am really stuck
to the following efficient issue.
Problem:
Given integers n, l and r, find the number of ways to represent n as a sum of two integers A and B such that l ≤ A ≤ B ≤ r.
Example:
For n = 6, l = 2 and r = 4, the output should be
countSumOfTwoRepresentations2(n, l, r) = 2.
There are just two ways to write 6 as A + B, where 2 ≤ A ≤ B ≤ 4: 6 = 2 + 4 and 6 = 3 + 3.
Here is my code. It passes all the unit tests but it failing
in the hidden ones. Can someone direct me somehow?
Thanks in advance.
public static int countSumOfTwoRepresentations2(int n, int l, int r) {
int nrOfWays = 0;
for(int i=l;i<=r;i++)
{
for(int j=i;j<=r;j++)
{
if(i+j==n)
nrOfWays++;
}
}
return nrOfWays;
}
Well, there's no need to make so huge calculations... It's easy to calculate:
public static int count(int n, int l, int r) {
if (l > n/2)
return 0;
return Math.min(n/2 - l, r - n/2) + ((n%2 == 1) ? 0 : 1);
}
Passes all my tests so far. For positives and negatives as well.
In java:
int countSumOfTwoRepresentations2(int n, int l, int r)
{
return Math.max(0,Math.min(n/2-l,r-n/2)+(n+1)%2);
}
In Python3:
def countSumOfTwoRepresentations2(n, l, r):
return max(0,min(n//2-l,r-n//2)+(n+1)%2)
int countSumOfTwoRepresentations(int n, int l, int r)
{
int r1 = 0;
if (n > l + r || n < 2 * l)
return 0;
r1 = n - l;
if ((r1 - l) % 2 == 0)
return (r1 - l) / 2 + 1;
else
return (r1 - l + 1) / 2;
}
Here my solution using a hash table. No as efficient as the others, but easier to understand. Based on the famous leetcode "2sum" problem.
https://leetcode.com/problems/two-sum/
fun solution(n: Int, l: Int, r: Int): Int {
val h = hashMapOf<Int, Int>()
for (i in l..r)
{
val c = n-i
if (c >=l && c<=r)
{
if (h[i] == null)
{
h.put(c, i)
}
}
}
return h.size
}

Using Binary Search with sorted Array with duplicates [duplicate]

This question already has answers here:
Finding multiple entries with binary search
(15 answers)
Closed 3 years ago.
I've been tasked with creating a method that will print all the indices where value x is found in a sorted array.
I understand that if we just scanned through the array from 0 to N (length of array) it would have a running time of O(n) worst case. Since the array that will be passed into the method will be sorted, I'm assuming that I can take advantage of using a Binary Search since this will be O(log n). However, this only works if the array has unique values. Since the Binary Search will finish after the first "find" of a particular value. I was thinking of doing a Binary Search for finding x in the sorted array, and then checking all values before and after this index, but then if the array contained all x values, it doesn't seem like it would be that much better.
I guess what I'm asking is, is there a better way to find all the indices for a particular value in a sorted array that is better than O(n)?
public void PrintIndicesForValue42(int[] sortedArrayOfInts)
{
// search through the sortedArrayOfInts
// print all indices where we find the number 42.
}
Ex: sortedArray = { 1, 13, 42, 42, 42, 77, 78 } would print: "42 was found at Indices: 2, 3, 4"
You will get the result in O(lg n)
public static void PrintIndicesForValue(int[] numbers, int target) {
if (numbers == null)
return;
int low = 0, high = numbers.length - 1;
// get the start index of target number
int startIndex = -1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
startIndex = mid;
high = mid - 1;
} else
low = mid + 1;
}
// get the end index of target number
int endIndex = -1;
low = 0;
high = numbers.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
endIndex = mid;
low = mid + 1;
} else
low = mid + 1;
}
if (startIndex != -1 && endIndex != -1){
for(int i=0; i+startIndex<=endIndex;i++){
if(i>0)
System.out.print(',');
System.out.print(i+startIndex);
}
}
}
Well, if you actually do have a sorted array, you can do a binary search until you find one of the indexes you're looking for, and from there, the rest should be easy to find since they're all next to each-other.
once you've found your first one, than you go find all the instances before it, and then all the instances after it.
Using that method you should get roughly O(lg(n)+k) where k is the number of occurrences of the value that you're searching for.
EDIT:
And, No, you will never be able to access all k values in anything less than O(k) time.
Second edit: so that I can feel as though I'm actually contributing something useful:
Instead of just searching for the first and last occurrences of X than you can do a binary search for the first occurence and a binary search for the last occurrence. which will result in O(lg(n)) total. once you've done that, you'll know that all the between indexes also contain X(assuming that it's sorted)
You can do this by searching checking if the value is equal to x , AND checking if the value to the left(or right depending on whether you're looking for the first occurrence or the last occurrence) is equal to x.
public void PrintIndicesForValue42(int[] sortedArrayOfInts) {
int index_occurrence_of_42 = left = right = binarySearch(sortedArrayOfInts, 42);
while (left - 1 >= 0) {
if (sortedArrayOfInts[left-1] == 42)
left--;
}
while (right + 1 < sortedArrayOfInts.length) {
if (sortedArrayOfInts[right+1] == 42)
right++;
}
System.out.println("Indices are from: " + left + " to " + right);
}
This would run in O(log(n) + #occurrences)
Read and understand the code. It's simple enough.
Below is the java code which returns the range for which the search-key is spread in the given sorted array:
public static int doBinarySearchRec(int[] array, int start, int end, int n) {
if (start > end) {
return -1;
}
int mid = start + (end - start) / 2;
if (n == array[mid]) {
return mid;
} else if (n < array[mid]) {
return doBinarySearchRec(array, start, mid - 1, n);
} else {
return doBinarySearchRec(array, mid + 1, end, n);
}
}
/**
* Given a sorted array with duplicates and a number, find the range in the
* form of (startIndex, endIndex) of that number. For example,
*
* find_range({0 2 3 3 3 10 10}, 3) should return (2,4). find_range({0 2 3 3
* 3 10 10}, 6) should return (-1,-1). The array and the number of
* duplicates can be large.
*
*/
public static int[] binarySearchArrayWithDup(int[] array, int n) {
if (null == array) {
return null;
}
int firstMatch = doBinarySearchRec(array, 0, array.length - 1, n);
int[] resultArray = { -1, -1 };
if (firstMatch == -1) {
return resultArray;
}
int leftMost = firstMatch;
int rightMost = firstMatch;
for (int result = doBinarySearchRec(array, 0, leftMost - 1, n); result != -1;) {
leftMost = result;
result = doBinarySearchRec(array, 0, leftMost - 1, n);
}
for (int result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n); result != -1;) {
rightMost = result;
result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n);
}
resultArray[0] = leftMost;
resultArray[1] = rightMost;
return resultArray;
}
Another result for log(n) binary search for leftmost target and rightmost target. This is in C++, but I think it is quite readable.
The idea is that we always end up when left = right + 1. So, to find leftmost target, if we can move right to rightmost number which is less than target, left will be at the leftmost target.
For leftmost target:
int binary_search(vector<int>& nums, int target){
int n = nums.size();
int left = 0, right = n - 1;
// carry right to the greatest number which is less than target.
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, right is at the index of greatest number
// which is less than target and since left is at the next,
// it is at the first target's index
return left;
}
For the rightmost target, the idea is very similar:
int binary_search(vector<int>& nums, int target){
while(left <= right){
int mid = (left + right) / 2;
// carry left to the smallest number which is greater than target.
if(nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, left is at the index of smallest number
// which is greater than target and since right is at the next,
// it is at the first target's index
return right;
}
I came up with the solution using binary search, only thing is to do the binary search on both the sides if the match is found.
public static void main(String[] args) {
int a[] ={1,2,2,5,5,6,8,9,10};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 2));
System.out.println(5+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 5));
int a1[] ={2,2,2,2,2,2,2,2,2};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a1, 0, a1.length-1, 2));
int a2[] ={1,2,3,4,5,6,7,8,9};
System.out.println(10+" IS AVAILABLE AT = "+findDuplicateOfN(a2, 0, a2.length-1, 10));
}
public static String findDuplicateOfN(int[] a, int l, int h, int x){
if(l>h){
return "";
}
int m = (h-l)/2+l;
if(a[m] == x){
String matchedIndexs = ""+m;
matchedIndexs = matchedIndexs+findDuplicateOfN(a, l, m-1, x);
matchedIndexs = matchedIndexs+findDuplicateOfN(a, m+1, h, x);
return matchedIndexs;
}else if(a[m]>x){
return findDuplicateOfN(a, l, m-1, x);
}else{
return findDuplicateOfN(a, m+1, h, x);
}
}
2 IS AVAILABLE AT = 12
5 IS AVAILABLE AT = 43
2 IS AVAILABLE AT = 410236578
10 IS AVAILABLE AT =
I think this is still providing the results in O(logn) complexity.
A Hashmap might work, if you're not required to use a binary search.
Create a HashMap where the Key is the value itself, and then value is an array of indices where that value is in the array. Loop through your array, updating each array in the HashMap for each value.
Lookup time for the indices for each value will be ~ O(1), and creating the map itself will be ~ O(n).
Find_Key(int arr[], int size, int key){
int begin = 0;
int end = size - 1;
int mid = end / 2;
int res = INT_MIN;
while (begin != mid)
{
if (arr[mid] < key)
begin = mid;
else
{
end = mid;
if(arr[mid] == key)
res = mid;
}
mid = (end + begin )/2;
}
return res;
}
Assuming the array of ints is in ascending sorted order; Returns the index of the first index of key occurrence or INT_MIN. Runs in O(lg n).
It is using Modified Binary Search. It will be O(LogN). Space complexity will be O(1).
We are calling BinarySearchModified two times. One for finding start index of element and another for finding end index of element.
private static int BinarySearchModified(int[] input, double toSearch)
{
int start = 0;
int end = input.Length - 1;
while (start <= end)
{
int mid = start + (end - start)/2;
if (toSearch < input[mid]) end = mid - 1;
else start = mid + 1;
}
return start;
}
public static Result GetRange(int[] input, int toSearch)
{
if (input == null) return new Result(-1, -1);
int low = BinarySearchModified(input, toSearch - 0.5);
if ((low >= input.Length) || (input[low] != toSearch)) return new Result(-1, -1);
int high = BinarySearchModified(input, toSearch + 0.5);
return new Result(low, high - 1);
}
public struct Result
{
public int LowIndex;
public int HighIndex;
public Result(int low, int high)
{
LowIndex = low;
HighIndex = high;
}
}
public void printCopies(int[] array)
{
HashMap<Integer, Integer> memberMap = new HashMap<Integer, Integer>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
memberMap.put(array[i], 1);
else
{
int temp = memberMap.get(array[i]); //get the number of occurances
memberMap.put(array[i], ++temp); //increment his occurance
}
//check keys which occured more than once
//dump them in a ArrayList
//return this ArrayList
}
Alternatevely, instead of counting the number of occurances, you can put their indices in a arraylist and put that in the map instead of the count.
HashMap<Integer, ArrayList<Integer>>
//the integer is the value, the arraylist a list of their indices
public void printCopies(int[] array)
{
HashMap<Integer, ArrayList<Integer>> memberMap = new HashMap<Integer, ArrayList<Integer>>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
{
ArrayList temp = new ArrayList();
temp.add(i);
memberMap.put(array[i], temp);
}
else
{
ArrayList temp = memberMap.get(array[i]); //get the lsit of indices
temp.add(i);
memberMap.put(array[i], temp); //update the index list
}
//check keys which return lists with length > 1
//handle the result any way you want
}
heh, i guess this will have to be posted.
int predefinedDuplicate = //value here;
int index = Arrays.binarySearch(array, predefinedDuplicate);
int leftIndex, rightIndex;
//search left
for(leftIndex = index; array[leftIndex] == array[index]; leftIndex--); //let it run thru it
//leftIndex is now the first different element to the left of this duplicate number string
for(rightIndex = index; array[rightIndex] == array[index]; rightIndex++); //let it run thru it
//right index contains the first different element to the right of the string
//you can arraycopy this [leftIndex+1, rightIndex-1] string or just print it
for(int i = leftIndex+1; i<rightIndex; i++)
System.out.println(array[i] + "\t");

Combine QuickSort and Median selection algorithm

I want to modify QuickSort (in Java) so that every time Partition is called, the median of the proportioned array is used as the pivot.
I have a median selection algorithm in Java that returns the kth smallest element, in this case the median. I have tons of quicksort algorithms in java that all work by themselves and sort an array. Unfortunately I can't combine those two in order to achieve the above... Everytime I try it i usually get stackoverflow erros.
Can anybody show me code to see how it can be done?
Thanks
EDIT: For example this is a median selection algorithm that I have tried to use.
public int quickSelect(int[] A, int p, int r, int k) {
if (p==r) return A[p];
int q = Partition(A,p,r);
int len = q-p+1;
if (k == len) return A[q];
else if (k<len) return Select(A,p,q-1,k);
else return Select(A,q+1,r,k-len);
}
public int partition(int[]A, int p, int r) {
int x = A[r];
int i = p-1;
for (int j = p; j<=r-1; j++) {
if (A[j] <= x) {
i++;
swap(A,i,j);
}
}
swap(A,i+1,r);
return i+1;
}
It works by itself but when I try to call quickSelect through quicksort's partition function to return the pivot to be used, it doesn't work. Obviously I'm doing something wrong but I don't know what. Unfortunately on the Internet I haven't found any algorithm, even in pseudocode, that would combine a median selection with quicksort.
The standard way to get the median is to sort the data. And you want to sort the data by partitioning on the median. This seems very chicken and egg to me.
Could you elaborate on why you want to partition/pivot on the median?
What you are looking for is the Selection Algorithm. Here's a link with pseudocode.
From the link:
In computer science, a selection algorithm is an algorithm for finding the kth smallest number in a list
To find the median you want to find the k=floor((n+1)/2) smallest number in the list where n is the size of the list.
Note that in PARTITION the pivot is A[r].
public int QUICKSORT2(int[] A, int p, int r) {
if (p<r) {
int median=Math.floor((p + r) /2) - p + 1
int q=SELECT(A, p, r, median)
q=PARTITION2(A, p, r, q)
QUICKSORT2(A, p, q-1)
QUICKSORT2(A, q+1, r)
}
}
public int PARTITION2(int[]A, int p, int r, int q) {
int temp = A[r];
A[r]=A[q];
A[q]=temp;
return PARTITION(A, p, r)
}
You can use this ...
int Select(int array[],int start, int end,int k){
if(start==end){
return start;
}
int x=array[end];
int i=start-1;
for(int j=start;j<=end-1;j++){
if(array[j]<x){
i++;
Swap(array+i,array+j);
}
}
i++;
Swap(array+i,array+end);
if(i==k){
return i;
}
else if(i>k){
return Select(array,start,i-1,k);
}
else{
return Select(array,i+1,end,k);
}
}
Select will partition array on kth smallest element in array;

Categories

Resources