Issues with Binary Search Algorithm - java

I have an assignment to write a binary search that returns the first iteration of the value we are looking for. I've been doing some research online and my search looks a lot like what i'm finding but i'm having an issue. If I pass this code an array that looks like this {10,5,5,3,2} it find the 5 at in the middle(The first thing it checks) and then just returns it. But that is not the first iteration of the 5 it is the second. What am I doing wrong? Is this even possible?
Thanks in advance!
The code(I'm using Java):
public static int binarySearch(int[] arr, int v){
int lo = 0;
int hi = arr.length-1;
while(lo <= hi){
int middle = (lo+hi)/2;
if(v == arr[middle]){
return middle;
}
else
{
if(v < arr[middle]){
lo = middle+1;
}
else
{
hi = middle-1;
}
}
}
return -1;
}

Here is a modified algorithm that works.
public static int binarySearch(int[] arr, int v) {
int lo = -1;
int hi = arr.length - 1;
while (hi - lo > 1 ) {
int middle = (lo + hi) / 2;
if (arr[middle] > v) {
lo = middle;
} else {
hi = middle;
}
}
if (v == arr[hi]) {
return hi;
} else {
return -1;
}
}
The key points are:
The interval (lo, hi] is exclusive to the left, inclusive to the right.
At each step we throw away one half of the interval. We stop when we are down to one element. Attempts to terminate early offer only a minimal performance boost, while they often affect code legibility and/or introduce bugs.
When arr[middle] = v we assign hi = middle, thus throwing away the right half. This is safe to do because we don't care any occurrences of v past middle. We do care about arr[middle], which may or may not be the first occurrence, and it is for this reason that we made (lo, hi] inclusive to the right. If there are occurrences of v before middle, we will find them in subsequent iterations.
As a side note, the more natural definition [0, n) inclusive to the left, exclusive to the right, can be used to find the last occurrence of v.
In my experience, this inclusive-exclusive interval definition produces the shortest, clearest and most versatile code. People keep trying to improve on it, but they often get tangled up in corner cases.

Related

Group integers in a given array where no 2 numbers in the same group has a difference more than k. Find the minimum number of groups

I'm working on an assignment and really struggling coming up with a working solution. I will explain the question first and then walk through how I am thinking about the solution.
Question: Input: List<Integer> nums = [1,5,4,6,8,9,2] k = 3
I have to find the minimum number of groups, where no 2 numbers in the same group has a difference more than 3. So, in here, one of the solutions could be [[1,2,4],[5,6,8],[9]]. Note that it can also be [[1,2,4],[5,6],[8,9]]. Either way minimum number of groups is 3.
My Strategy: I am thinking of using binary search recursive way. To briefly explain what I have been trying to do -
- Sort the Array. So it becomes - [1,2,4,5,6,8,9]
- Make left = 0 right = nums.size()-1
- Create base cases
- if size of the array is 0 return 0
- if size of the array is 1 return 1
- if nums.get(right) - nums.get(left) <= k return 1
- While left < right
- Make mid = (left+right)/2
- if (number at mid - number at left) is greater than k
-recursively call the function like this, findMinGroup(nums, left, mid-1, k)
-At one point this recursive call will hit one of the base cases and return - either 0 or 1.
-if it returns 1 that means we found a group. if It returns 0 that means in the left side we can not form any group. So we check the right side.
This is the code I have written so far,
public class GroupNumbers {
static int minNumGroups = 0;
public static int minimumGroups(List<Integer> nums, int k){
Collections.sort(nums);
System.out.println(nums);
int left = 0;
int right = nums.size() == 0 ? nums.size() : nums.size() - 1;
return findMinGroups(nums, left, right, k);
}
private static int findMinGroups(List<Integer> nums, int left, int right, int k){
if((right - left) == 0)
return 0;
if((right - left) == 1 || nums.get(right) - nums.get(left) <= k)
return 1;
while(left < right){
int mid = (left + right)/2;
if(nums.get(mid) - nums.get(left) > k){
int group = findMinGroups(nums, left, mid-1, k);
if(group > 0){
minNumGroups += group;
left = mid;
}else{
left = (left + right)/2;
}
}
}
return minNumGroups;
}
I feel like I have the right idea about the solutions, but due to my lack of experience with these type of algorithms, I am not able to fully articulate my thoughts in code. I would really appreciate some help/insight for solving this problem.
One other simple solution would be iteration over entire list after sorting.
public static int minimumGroups(List<Integer> nums, int k){
int minGrps = 1;
Collections.sort(nums);
int numToCmp = nums.get(0);
for(Integer num : nums) {
if(num > numToCmp + k) {
minGrps++;
numToCmp = num;
}
}
return minGrps;
}

Peak finding algorithm error

Hello everybody could someone help me with my code
It is a code for peak finding ,if you are wondering what is peak finding
here you go
Given an array of integers. Find a peak element in it. An array element is peak if it is NOT smaller than its neighbors. For corner elements, we need to consider only one neighbor. For example, for input array {5, 10, 20, 15}, 20 is the only peak element. For input array {10, 20, 15, 2, 23, 90, 67}, there are two peak elements: 20 and 90. Note that we need to return any one peak element.
My problem is that my code doesnt find a peak element if
it is in first position in array or in last
Here is my code it is fairly simple
public static void main(String[] args) {
int [] arr = {1,2,3,4,1,3,3,7,8,2,16};
peakFinding(arr, 0,arr.length);
}
public static void peakFinding(int [] arr,int start ,int end){
int mid = (start+end)/2;
if(arr[mid]<=arr[mid+1]){
start = mid;
end = arr.length;
peakFinding(arr, start, end);
}else if(arr[mid]<=arr[mid-1]){
start = 0;
end = mid-1;
peakFinding(arr, start, end);
}else{
System.out.println("I have found peak "+arr[mid]);
}
}
Given that you only need to find one element, and the choice is arbitrary, consider treating the edges as a special case. Before the call to peakFinding include code of the form
if (arr == null || arr.length < 2){
/*do nothing, no elements*/
} else if (arr[0] >= arr[1]){
/*first element is peak*/
} else if (arr[arr.length - 1] >= arr[arr.length - 2]){
/*last element is peak*/
} else {
/*call peakFinding*/
}
My first check also fixes a potential bug that you had.
Doing it this way preserves the clarity of the complicated parts of the program.
Finally, consider changing the return type of peakFinding to return the position of the element (return mid;), then the output message will be coded in one place.
Before answering your question, I feel like the starting part of your peakFinding does not look very good, instead of
int mid = (start+end)/2;
which might be problematic if start and end is too big and close to Integer.MAX_VALUE, please try
int mid = start + (end - start) / 2;
Also if you add some code to validate the input (null check of the array, or start <= end something like that), it would be better.
Now let's talk about your algorithm, it is a binary search.
public static void peakFinding(int [] arr,int start ,int end){
while(start + 1 < end) {
int mid = start + (end - start) / 2;
if(arr[mid] <= arr[mid+1]) {
start = mid;
} else if (arr[mid] <= arr[mid-1]) {
end = mid;
} else {
System.out.println(arr[mid]);
return;
}
}
if(arr[start] > arr[end]) {
System.out.println(arr[start]);
} else {
System.out.println(arr[end]);
}
}
Your program does not find all peaks.
If e.g. the condition arr[mid] <= arr[mid + 1] in the first if clause returns true, this means that a peak is searched only in the right part of the array. The left part of the array is no longer searched, although there could be a peak also in the left side of the array.

How to Check Array for 3 Numbers that Add to a Specific value in JAVA?

Alright guys, I've been stuck on this problem for a while now and have not been able to get past it. This is for Java. I'd appreciate any help at this point. Here are the details: Please note, we must do this in O(n) running time. We are given an array of numbers and must go through it to determine if there are any 3 numbers that sum to a specific number. HOWEVER, we are allowed to reuse any number in the array up to 3 times because we need a total of 3 numbers. We also have to output which 3 numbers gave the sum. Returning true or false.
Below is what I've got:
Do you guys have any suggestions?
You can make a for loop inside of a for loop inside of a for loop. This is for school, so I wont give you the code, but I'll give you the pseudo.
Edit: missed the O(n) part, sorry. This way should work.
public static void main(String[] args)
{
int[] test = {1,8,2,3,11,4};
System.out.println(threeSumTo(test, 6));
}
//check if 3 numbs in an array add up to int x
public static boolean threeSumTo(int[] array, int x)
{
//loop through the array
for (int i = 0; i < array.length; i++) {
boolean result = twoSumTo(array, x - array[i], i);
if (result) {
return result;
}
}
return false;
}
private static boolean twoSumTo(int[] array, int x, int low) {
int high = array.length - 1;
while (low < high) {
if (array[low] + array[high] == x) {
return true;
}
if (array[low] + array[high] > x) {
high--;
} else {
low++;
}
}
return false;
}
}
This seems to be a variation of the 3SUM problem and should obey the same restrictions.
Computing the 3SUM problem in less than O(n^2) is still a unsvoled problem.
Did your teacher ask a trick question or is that some kind of competition?
This is called a 3 sum problem and solving this problem in O(N) is impossible till now. The best you can do is O(N^2).
Check this article out.

Quicksort with insertion Sort finish - where am I going wrong?

I am working on a project for a class. We are to write a quick-sort that transitions to a insertion sort at the specified value. Thats no problem, where I am now having difficulty is figuring out why I am not getting the performance I expect.
One of the requirements is that it must sort an array of 5,00,000 ints in under 1,300 ms (this is on standard machines, so CPU speed is not an issue). First of all, I can't get it to work on 5,000,000 because of a stack overflow error (too many recursive calls...). If I increase the heap size, I am still getting a lot slower than that.
Below is the code. Any hints anyone?
Thanks in advance
public class MyQuickSort {
public static void sort(int [] toSort, int moveToInsertion)
{
sort(toSort, 0, toSort.length - 1, moveToInsertion);
}
private static void sort(int[] toSort, int first, int last, int moveToInsertion)
{
if (first < last)
{
if ((last - first) < moveToInsertion)
{
insertionSort(toSort, first, last);
}
else
{
int split = quickHelper(toSort, first, last);
sort(toSort, first, split - 1, moveToInsertion);
sort(toSort, split + 1, last, moveToInsertion);
}
}
}
private static int quickHelper(int[] toSort, int first, int last)
{
sortPivot(toSort, first, last);
swap(toSort, first, first + (last - first)/2);
int left = first;
int right = last;
int pivotVal = toSort[first];
do
{
while ( (left < last) && (toSort[left] <= pivotVal))
{
left++;
}
while (toSort[right] > pivotVal)
{
right--;
}
if (left < right)
{
swap(toSort, left, right);
}
} while (left < right);
swap(toSort, first, right);
return right;
}
private static void sortPivot(int[] toSort, int first, int last)
{
int middle = first + (last - first)/2;
if (toSort[middle] < toSort[first]) swap(toSort, first, middle);
if (toSort[last] < toSort[middle]) swap(toSort, middle, last);
if (toSort[middle] < toSort[first]) swap(toSort, first, middle);
}
private static void insertionSort(int [] toSort, int first, int last)
{
for (int nextVal = first + 1; nextVal <= last; nextVal++)
{
int toInsert = toSort[nextVal];
int j = nextVal - 1;
while (j >= 0 && toInsert < toSort[j])
{
toSort[j + 1] = toSort[j];
j--;
}
toSort[j + 1] = toInsert;
}
}
private static void swap(int[] toSort, int i, int j)
{
int temp = toSort[i];
toSort[i] = toSort[j];
toSort[j] = temp;
}
}
I haven't tested this with your algorithm, and I don't know what kind of data set you're running with, but consider choosing a better pivot than the leftmost element. From Wikipedia on Quicksort:
Choice of pivot In very early versions
of quicksort, the leftmost element of
the partition would often be chosen as
the pivot element. Unfortunately, this
causes worst-case behavior on already
sorted arrays, which is a rather
common use-case. The problem was
easily solved by choosing either a
random index for the pivot, choosing
the middle index of the partition or
(especially for longer partitions)
choosing the median of the first,
middle and last element of the
partition for the pivot
Figured it out.
Actually, not my sorts fault at all. I was generating numbers between the range of 0-100 (for testing to make sure it was sorted). This resulted in tons of duplicates, which meant way to many partitions. Changing the range to min_int and max_int made it go a lot quicker.
Thanks for your help though :D
When the input array is large, its natural to expect that recursive functions run into stack overflow issues. which is what is happening here when you try with the above code. I would recommend you to write iterative Quicksort using your own stack. It should be fast because there is no stack frame allocations/deallocations done at run time. You won't run into stack overflow issues also. Performance also depends on at what point you are running insertion sort. I don't have a particular input size where insertion sort performs badly compared to quicksort. I would suggest you to try with different sizes and I'm sure you will notice difference.
You might also want to use binary search in insertion sort to improve performance. I don't know how much it improves when you run on smaller input but its a nice trick to play.
I don't want to share code because that doesn't make you learn how to convert recursive quicksort to iterative one. If you have problems in converting to iterative one let me know.

Help In Solving Algorithm

Am suppose to return the number of character comparisons. In the while() loop i compare the index of the characters and update the counter. My question is, is it right to do it that way or i have to compare the characters themselves. I think comparing the index and updating the counter is the same as comparing the characters themselves. Any idea?
Need help.
The following is the code of the algorithm.
// Sort an array of strings using quick sort
// Always pivot on the first element
// Return the number of CHARACTER comparisons
public int stringQuickSort(ComparableByte[][] strings) {
Counter nCompares = new Counter();
sortStringQuickSort(strings, 0, strings.length-1, 0, nCompares, false);
return nCompares.value;
}
public void sortStringQuickSort(ComparableByte[][] strings, int lo, int hi, int d, Counter nCompares, boolean switchToOtherAlgorithm){
if(!switchToOtherAlgorithm){
if(hi <= lo)
return;
}else if(hi <= lo+10){
stringInsertionSort(strings);
return;
}
int lt = lo, gt = hi;
int v = characterAt(ComparableByte.toString(strings[lo]), d);
int i = lo+1;
while(i <= gt){
int t = characterAt(ComparableByte.toString(strings[i]), d);
nCompares.value++;
if (t < v){
swapTwoComparableByteElements(strings, lt++, i++);
nCompares.value++;
}
else if(t > v){
swapTwoComparableByteElements(strings, i, gt--);
}
else
i++;
}
sortStringQuickSort(strings, lo, lt-1, d, nCompares, switchToOtherAlgorithm);
if(v >= 0){
sortStringQuickSort(strings, lt, gt, d+1, nCompares, switchToOtherAlgorithm);
}
sortStringQuickSort(strings, gt+1, hi, d, nCompares, switchToOtherAlgorithm);
}
Thanks for your help
Usually these things are used to provide an empirical estimate of comparisons needed by algoritms to study if the expected behaviour is correct.
So, yes, it doesn't matter exactly what you count if you take care to count the correct operations. You don't care if they are characters of indices as long as you are actually counting whenever you program needs to compute a < > <= >= = operation related to the input and the current implementation.

Categories