Binary Search Specifics - java

I am hoping someone can help me understand this code for binary search. This is from LeetCode, it is "Template 2" under Binary Search.
I was wondering why you set right to nums.length
-Is there a reason why it isn't nums.length - 1 ?
int binarySearch(int[] nums, int target){
if(nums == null || nums.length == 0)
return -1;
int left = 0, right = nums.length;
while(left < right){
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] == target){ return mid; }
else if(nums[mid] < target) { left = mid + 1; }
else { right = mid; }
}
// Post-processing:
// End Condition: left == right
if(left != nums.length && nums[left] == target) return left;
return -1;
}

Related

Is there a better way of solving this The Leetcode ThreeSum problem?

Please I am just curious if there is a better way I can achieve the Leetcode ThreeSum problem.
I came up with this piece of code using Binary Search
// Time Complexity: O(nlogn) + O(nlogn) == O(nlogn)
// Extra Space Complexity: O(1)
public static List<List<Integer>> threeSumOptimalSolution(int[] nums) {
int n = nums.length;
if (n < 3)
return Collections.emptyList();
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
int left;
int right;
int mid;
int threeSum;
for (int firstValue = 0; firstValue < n - 2; firstValue++) {
if (firstValue == 0 || nums[firstValue] != nums[firstValue - 1]) {
left = firstValue + 1;
right = n - 1;
while (left < right) {
mid = left + (right - left) / 2;
threeSum = nums[firstValue] + nums[left] + nums[right];
if (threeSum < 0) {
if (nums[mid] + nums[right] + nums[firstValue] < 0)
left = mid + 1;
else
left++;
} else if (threeSum > 0) {
if (nums[mid] + nums[left] + nums[firstValue] > 0)
right = mid - 1;
else
right--;
} else {
result.add(List.of(nums[firstValue], nums[left], nums[right]));
left++;
while (nums[left] == nums[left - 1] && left < right)
left++;
while (nums[right] == nums[right - 1] && left < right)
right--;
}
}
}
}
return result;
}
I would appreciate an extensive review from individual/individuals.
The best time complexity in the ThreeNums problem I know is O(n^2)
And I think you want to use Two Pointers to finish the problem.
In your Answer, the part Of Binary Search is wrong, I don't know what's are you doing.
The correct method is to traverse the list outside and use Two Pointers inside.
E.g
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++) {
if(nums[i] > 0) break;
if(i > 0 && nums[i] == nums[i-1]) continue;
int left = i+1;
int right = nums.length-1;
while(left < right) {
int sum = nums[i] + nums[left] + nums[right];
if(sum == 0) {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[left]);
temp.add(nums[right]);
res.add(temp);
while(right > left && nums[right] == nums[right-1]) right--;
while(right > left && nums[left] == nums[left+1]) left++;
right--;
left++;
} else if(sum > 0) {
right--;
} else {
left++;
}
}
}
return res;
}
}

Why is my Binary search stuck in an endless loop?

I just had to pop in here to hopefully get a quick answer to my little problem. I'm trying to create a binary search method but ran into some problems. I created it iteratively, which my IDE(Intellij) apparently didn't like. It had me stuck in a endless loop of... well, you know the rest. Any suggestions?
Here is my simple, yet beautiful little snippet of code:
static int searchIt(int[] arr, int target){
int left = 0;
int right = arr.length - 1;
while (left <= right){
int mid = (right + left) / 2;
if(arr[mid] == target) {
return mid;
} else if(target < arr[mid]){
right = mid-1;
} else{
left = mid -1;
}
}
return -1;
}
No errors, not in runtime or compile time, just endless nothingness...
left value should be mid + 1 since if the your middle your target value greater than your mid value then you need to search the element in second half of your array
static int searchIt(int[] arr, int target){
int left = 0;
int right = arr.length - 1;
while (left <= right){
int mid = (right + left) / 2;
if(arr[mid] == target) {
return mid;
} else if(target < arr[mid]){
right = mid - 1;
} else{
left = mid + 1;
}
}
return -1;
}

Floor in a Sorted Array

I have a sorted array arr[] of size n without duplicates, and given a value x. Floor of x is defined as the largest element K in arr[] such that K is smaller than or equal to x. Find the index of K(0-based indexing).
Input:
n = 7
x = 0
arr[] = {1,2,8,10,11,12,19}
Output:
1
I have to solve it in O(logn).
The code which I have below passes the test cases but it's showing time limit exceeded. What is wrong with this code?
static int findFloor(long arr[], int n, long x) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] > x) {
if (mid != 0 && arr[mid - 1] < x) {
return mid;
} else {
high = mid - 1;
}
} else if (arr[mid] < x) {
low = mid + 1;
}
}
return -1;
}
Your program will stuck in infinite for the case arr[mid] == x as you haven't handled this case. Also, I don't see this program work properly for other cases also.
For example, if I give x=7, this will return the index 2, which is wrong. The correct index should be 1 as x=7 is greater then arr[1] == 2 and less than arr[2] == 8.
Also, your code will break if I give a input of greater than the last element in the array, say x=50 in your example. You'll get an ArrayIndexOutOfBoundException as your low will increase and make the mid index out of bound.
I've corrected the aforementioned cases and the fixed function is like below:
static int findFloor(long[] arr, int n, long x) {
if (x >= arr[arr.length - 1]) {
return arr.length - 1;
}
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] > x) {
if (mid != 0 && arr[mid - 1] < x) {
return mid;
} else {
high = mid - 1;
}
} else {
if (arr[mid] == x || (mid != 0 && arr[mid + 1] > x)) {
return mid;
} else {
low = mid + 1;
}
}
}
return -1;
}
Hope you got your answer.

How to use Binary Search to find duplicates in sorted array?

I am attempting to expand a function to find the number of integer matches through Binary Search by resetting the high variable, but it gets stuck in a loop. I am guessing a workaround would be to duplicate this function to obtain the last index to determine the number of matches, but I do not think this would be such an elegant solution.
From this:
public static Matches findMatches(int[] values, int query) {
int firstMatchIndex = -1;
int lastMatchIndex = -1;
int numberOfMatches = 0;
int low = 0;
int mid = 0;
int high = values[values.length - 1];
boolean searchFirst = false;
while (low <= high){
mid = (low + high)/2;
if (values[mid] == query && firstMatchIndex == -1){
firstMatchIndex = mid;
if (searchFirst){
high = mid - 1;
searchFirst = false;
} else {
low = mid + 1;
}
} else if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
}
if (firstMatchIndex != -1) { // First match index is set
return new Matches(firstMatchIndex, numberOfMatches);
}
else { // First match index is not set
return new Matches(-1, 0);
}
}
To something like this:
public static Matches findMatches(int[] values, int query) {
int firstMatchIndex = -1;
int lastMatchIndex = -1;
int numberOfMatches = 0;
int low = 0;
int mid = 0;
int high = values[values.length - 1];
boolean searchFirst = false;
while (low <= high){
mid = (low + high)/2;
if (values[mid] == query && firstMatchIndex == -1){
firstMatchIndex = mid;
if (searchFirst){
high = values[values.length - 1]; // This is stuck in a loop
searchFirst = false;
}
} else if (values[mid] == query && lastMatchIndex == -1){
lastMatchIndex = mid;
if (!searchFirst){
high = mid - 1;
} else {
low = mid + 1;
}
} else if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
}
if (firstMatchIndex != -1) { // First match index is set
return new Matches(firstMatchIndex, numberOfMatches);
}
else { // First match index is not set
return new Matches(-1, 0);
}
}
There is a problem with your code:
high = values[values.length - 1];
should be
high = values.length - 1;
Also you do not need variables like numberOfMatches and searchFirst, we can have rather simple solution.
Now coming to the problem,I understand what you want I think Binary Search is appropriate for such query.
The Best way to do the required is once a match is found you just go forward and backward from that index until a mismatch occurs and this would be both elegant and efficient in calculating the firstMatchIndex and numberOfMatches.
So your function should be:
public static Matches findMatches(int[] values, int query)
{
int firstMatchIndex = -1,lastMatchIndex=-1;
int low = 0,mid = 0,high = values.length - 1;
while (low <= high)
{
mid = (low + high)/2;
if(values[mid]==query)
{
lastMatchIndex=mid;
firstMatchIndex=mid;
while(lastMatchIndex+1<values.length&&values[lastMatchIndex+1]==query)
lastMatchIndex++;
while(firstMatchIndex-1>=0&&values[firstMatchIndex-1]==query)
firstMatchIndex--;
return new Matches(firstMatchIndex,lastMatchIndex-firstMatchIndex+1);
}
else if(values[mid]>query)
high=mid-1;
else low=mid+1;
}
return new Matches(-1,0);
}
Couldn't you just use something like a set to find duplicates?
Something like this:
package example;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class DuplicatesExample {
public static void main(String[] args) {
String[] strings = { "one", "two", "two", "three", "four", "five", "six", "six" };
List<String> dups = getDups(strings);
System.out.println("DUPLICATES:");
for(String str : dups) {
System.out.println("\t" + str);
}
}
private static List<String> getDups(String[] strings) {
ArrayList<String> rtn = new ArrayList<String>();
HashSet<String> set = new HashSet<>();
for (String str : strings) {
boolean added = set.add(str);
if (added == false ) {
rtn.add(str);
}
}
return rtn;
}
}
Output:
DUPLICATES:
two
six
I have split your problems into two parts - using binary search to find a number and counting the number of matches. The first part is resolved by the search function while the second part is resolved by the findMatches function:
public static Matches findMatches(int[] values, int query) {
int leftIndex = -1;
int rightIndex = -1;
int high = values.length - 1;
int matchedIndex = search(values, 0, high, query);
//if at least one match
if (matchedIndex != -1) {
//decrement upper bound of left array
int leftHigh = matchedIndex - 1;
//increment lower bound of right array
int rightLow = matchedIndex + 1;
//loop until no more duplicates in left array
while (true) {
int leftMatchedIndex = search(values, 0, leftHigh, query);
//if duplicate found
if (leftMatchedIndex != -1) {
leftIndex = leftMatchedIndex;
//decrement upper bound of left array
leftHigh = leftMatchedIndex - 1;
} else {
break;
}
}
//loop until no more duplicates in right array
while(true){
int rightMatchedIndex = search(values, rightLow, high, query);
//if duplicate found
if(rightMatchedIndex != -1){
rightIndex = rightMatchedIndex;
//increment lower bound of right array
rightLow = rightMatchedIndex + 1;
} else{
break;
}
}
return new Matches(matchedIndex, rightIndex - leftIndex + 1);
}
return new Matches(-1, 0);
}
private static int search(int[] values, int low, int high, int query) {
while (low <= high) {
int mid = (low + high) / 2;
if (values[mid] == query) {
return mid;
} else if (query < values[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
I found a solution after correcting a mistake with resetting high variable that caused an infinite loop.
public static Matches findMatches(int[] values, int query) {
int firstMatchIndex = -1;
int lastMatchIndex = -1;
int numberOfMatches = 0;
int low = 0;
int mid = 0;
int high = values.length - 1;
while (low <= high){
mid = (low + high)/2;
if (values[mid] == query && firstMatchIndex == -1){
firstMatchIndex = mid;
numberOfMatches++;
high = values.length - 1;
low = mid;
} else if (values[mid] == query && (lastMatchIndex == -1 || lastMatchIndex != -1)){
lastMatchIndex = mid;
numberOfMatches++;
if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
} else if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
}
if (firstMatchIndex != -1) { // First match index is set
return new Matches(firstMatchIndex, numberOfMatches);
}
else { // First match index is not set
return new Matches(-1, 0);
}
}
Not having any knowledge of the data other than sorted a priori is tough.
See this:
Binary Search O(log n) algorithm to find duplicate in sequential list?
This will find the first index of duplicates of k in a sorted array.
Of course this is related to knowing the value of duplicate first but very useful when it is known.
public static int searchFirstIndexOfK(int[] A, int k) {
int left = 0, right = A.length - 1, result = -1;
// [left : right] is the candidate set.
while (left <= right) {
int mid = left + ((right - left) >>> 1); // left + right >>> 1;
if (A[mid] > k) {
right = mid - 1;
} else if (A[mid] == k) {
result = mid;
right = mid - 1; // Nothing to the right of mid can be
// solution.
} else { // A[mid] < k
left = mid + 1;
}
}
return result;
}
This will find a dupe in log(n) time but is fragile in that the data must be sorted as well as increasing by 1 and in the range 1..n.
static int findeDupe(int[] array) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
if (array[mid] == mid) {
low = mid + 1;
} else {
high = mid - 1;
}
}
System.out.println("returning" + high);
return high;
}

QuickSort (Java) implementation either overflows or stops short

I have been reading through all of the QuickSort questions on SO, but I cannot resolve this specific problem. By referencing the other questions and comparing my faults to theirs I have gotten to a specific point, that I cannot find the answer to, even in Debug mode.
I was repeatedly getting out of bounds -1, so I added a conditional check for
if(pivot > 0)
and that stopped the overflow, but since I am using 0 as my partition, It partitions once and then terminates. The first partition is correct, but if I change that number to include 0, the I get infinite recursion again. If I completely take the line out, I get index out of bounds errors that I cannot seem to tackle.
Here's where I am so far:
public class QuickSort {
int[] array;
public static void main(String[] args) {
QuickSort qs = new QuickSort();
qs.array = new int[] {35, 82, 2, 24, 57, 17};
qs.quickSort(qs.array, 0, qs.array.length - 1);
for(int i = 0; i < qs.array.length; i++) {
System.out.println(qs.array[i]);
}
}
public void quickSort(int[] array, int left, int right) {
if(array.length == 1) {
return;
}
if(left < right) {
int pivot = partition(array, left, right);
quickSort(array, left, pivot - 1);
quickSort(array, pivot + 1, right);
}
}
public int partition(int[] array, int left, int right) {
if(array.length == 1) {
return right;
}
int pivot = array[0];
int pivotIndex = 0;
int leftPointer = left - 1;
int rightPointer = right + 1;
while(pivotIndex < right) {
if(leftPointer > rightPointer) {
break;
}
leftPointer++;
while(leftPointer < array.length - 1 && array[leftPointer] <= pivot) {
leftPointer++;
}
rightPointer--;
while(rightPointer > leftPointer && array[rightPointer] > pivot) {
rightPointer--;
}
if(leftPointer < rightPointer) {
int temp = array[leftPointer];
array[leftPointer] = array[rightPointer];
array[rightPointer] = temp;
} else {
int temp = array[rightPointer];
array[rightPointer] = array[pivotIndex];
array[pivotIndex] = temp;
}
}
return rightPointer;
}
EDIT: After a few more alterations, I can now get it to always return an array without overflow, but it still only partitions once.
I'm pretty sure I fixed it now. You were increasing the left and right pointers within the partition method before you wanted to (outside of the "checks"). Change your partition method as follows:
public static int partition(int[] array, int left, int right) {
if(array.length == 1)
return right;
int pivot = array[0];
int pivotIndex = 0;
int leftPointer = left; // Remove the +1
int rightPointer = right; // Remove the +1
while(pivotIndex < right) {
if(leftPointer > rightPointer) {
break;
}
//leftPointer++;
while((leftPointer < array.length - 1) && (array[leftPointer] <= pivot)) {
leftPointer++;
}
//rightPointer--;
while((rightPointer > leftPointer) && (array[rightPointer] > pivot)) {
rightPointer--;
}
if(leftPointer < rightPointer) {
int temp = array[leftPointer];
array[leftPointer] = array[rightPointer];
array[rightPointer] = temp;
}
else {
int temp = array[rightPointer];
array[rightPointer] = array[pivotIndex];
array[pivotIndex] = temp;
}
}
return rightPointer;
}
You're returning "0" from partition whenever the array length is not 1, and setting that to the pivot. The if(pivot >= 0) will always be hit in that case, or it will iterate once if you used if(pivot > 0), which I think is the problem. If that's right, then correcting your return from partition (to "left" ?) should fix the problem.
I think you should change
if(leftPointer > rightPointer) {
break;
}
to
if(leftPointer >= rightPointer) {
break;
}
inside the while loop.
Also, I think you should compare leftPointer with rightPointer after either is changed,
// move to #### to perform compare after possible change
// if(leftPointer > rightPointer) break;
//leftPointer++;
while(leftPointer < array.length - 1 && array[leftPointer] <= pivot) leftPointer++;
//rightPointer--;
while(rightPointer > leftPointer && array[rightPointer] > pivot) rightPointer--;
//####
if(leftPointer > rightPointer) break;

Categories