I am trying to implement a part of quicksort where I call a method called splitPoint. SplitPoint will use the first index of the array as the pivot value and the pivot will move to the center of the array. It will return the index of the new index of the pivot. However, If I have an array of length 2 and it is in descending, such as [2, 1], it fails to sort. The method works for everything else though. I think that if this does not work, my quicksort as a whole will not work.
public int splitPoint(int[] a, int first, int last){
int splitPoint = a[first];
int low = first + 1;
int high = last - 1;
int temp; //holds the temp val for swapping values
while(low < high){
while(a[low] <= splitPoint && low != last && high > low){
low++;
//System.out.println("While loop 1 tracer");
}
while(a[high] > splitPoint && high >= first && high >= low){
high--;
//System.out.println("While loop 2 tracer");
}
if(low <= high){
temp = a[low];
a[low] = a[high];
a[high] = temp;
low++;
high++;
}
System.out.println(Arrays.toString(a)); // tracer
}
a[first] = a[high];
a[high] = splitPoint;
return high;
}
If you check, you will see that for an input such as [5, 0, 3] it returns [0, 5, 3]. So there is a problem even for larger arrays.
A somewhat revised version of the code should work ok:
static int split(int[] array, final int first, final int last) {
int low = first;
int high = last;
int splitPoint = first;
while (low < high) {
while (array[high] > array[low] && high > low) {
high--;
}
while (array[low] <= array[high] && high > low) {
low++;
}
if (low < high) {
int tmp = array[low];
array[low] = array[high];
array[high] = tmp;
if (low == splitPoint) {
low++;
splitPoint = high;
} else {
high--;
splitPoint = low;
}
}
}
return splitPoint;
}
Example:
public static void main(String[] args) {
int[] array = new int[]{5, 0, 3};
System.out.println(split(array, 0, array.length - 1));
System.out.println(Arrays.toString(array));
}
Output:
2
[3, 0, 5]
Easy answer is to walk through your code.
Presuming your call looks like:
splitPoint({2, 1}, 0, 1);
int splitPoint = a[first]; // Will hold a value of 2.
int low = first + 1; //low = 0 + 1 = 1.
int high = last - 1; // high = 1 - 1 = 0.
int temp; //holds the temp val for swapping values
while(low < high) //<== here. low = 1. high = 0. low > high, test is false--loop is NOT performed.
a[first] = a[high]; // a[0] = a[0] = 2.
a[high] = splitPoint; //a[0] = 2
return high; //returns 0.
So, in short, your problem is in your initialization of low and high.
Related
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.
The instruction is to edit a quicksort program to select the median of three as the pivot as opposed to the first value of an array.
In order to do so, I've edited the code as follows:
public class medianOf3 {
//Main method and test array
public static void main(String[] args) {
int[] list = {2, 3, 2, 5, 6, 1, -2, 3, 14, 12};
quickSort(list);
for (int i = 0; i < list.length; i++)
System.out.print(list[i] + " ");
System.out.println();
}
public static void quickSort(int[] list) {
quickSort(list, 0, list.length - 1);
}
//The quicksort method
public static void quickSort(int[] list, int first, int last) {
if (last > first) {
int pivotIndex = partition(list, first, last);
quickSort(list, first, pivotIndex - 1);
quickSort(list, pivotIndex + 1, last);
}
}
// Returns the median of three integers
public static int median(int first, int middle, int last) {
return Math.max(Math.min(first, middle), Math.min(Math.max(first, middle), last));
}
//returns the index of a value
public static int findIndex (int[] list, int t) {
if (list == null) return -1;
int len = list.length;
int i = 0;
while (i < len) {
if (list[i] == t) return i;
else i=i+1;
}
return -1;
}
public static int partition(int[] list, int first, int last) {
int middle = ((last-first) / 2)+first;
int pivot = median(list[first], list[middle], list[last]); // selecting the median of three (of the first, middle and last value) as the pivot
int low = first +1; // Index for forward search
int high = last; // Index for backward search
int index = findIndex(list, pivot );
int swap = list[index];
list[index] = list[0];
list[0] = swap;
while (high > low) {
// Search forward from left
while (low <= high && list[low] <= pivot)
low++;
// Search backward from right
while (low <= high && list[high] > pivot)
high--;
// Swap two elements in the list
if (high > low) {
int temp = list[high];
list[high] = list[low];
list[low] = temp;
}
}
while (high > first && list[high] >= pivot)
high--;
// Swap pivot with list[high]
if (pivot > list[high]) {
list[first] = list[high];
list[high] = pivot;
return high;
} else { return first;}
}
}
The above code returns the following output:
14 1 2 2 3 3 5 6 12 14
The desired output is this:
-2 1 2 2 3 3 5 6 12 14
Using the debugger, I'm able to get within one calculation of the array being sorted correctly,
with only the last 2 values needing to be swapped:
-2 1 2 2 3 3 5 6 14 12
Within the final circulation at the
list[index] = list[0];
line of the partition method, is where the error occurs, using the debugger. I feel this is most likely a logical error but I'm uncertain of exactly what is going wrong at that point.
All feedback appreciated.
I suggest as solution (based in your code with few changes) :
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 3, 2, 5, 6, 1, -2, 3, 14, 12);
quickSort(list, 0, list.size() - 1);
System.out.println(list);
}
private static void quickSort(List<Integer> list, int first, int last) {
if (last - first > 0) {
int pivot = pivot(list, first, last);
int index = partition(list, first, last, pivot);
quickSort(list, first, index - 1);
quickSort(list, index + 1, last);
}
}
private static int pivot(List<Integer> list, int first, int last) {
return ((last - first) / 2) + first;
}
private static int partition(List<Integer> list, int first, int last, int pivot) {
Collections.swap(list, last, pivot);
int j = first;
for (int i = first; i < last; i++) {
if (list.get(i) <= list.get(last)) {
Collections.swap(list, i, j);
j++;
}
}
Collections.swap(list, last, j);
return j;
}
Alternate example with a single function and stack overflow prevention (worst case time complexity is still O(n^2)). In this example median of 3 is performed by sorting a[lo, md, hi], where md = (lo+hi)/2, which takes 3 if/swaps.
#SuppressWarnings("empty-statement")
public static void qsort(int[] a, int lo, int hi)
{
while(lo < hi){
int md = lo+(hi-lo)/2;
int ll = lo-1;
int hh = hi+1;
int t;
if(a[lo] > a[hi]){ // median of 3
t = a[lo];
a[lo] = a[hi];
a[hi] = t;
}
if(a[lo] > a[md]){
t = a[lo];
a[lo] = a[md];
a[md] = t;
}
if(a[md] > a[hi]){
t = a[md];
a[md] = a[hi];
a[hi] = t;
}
int p = a[md];
while(true){ // partition
while(a[++ll] < p);
while(a[--hh] > p);
if(ll >= hh)
break;
t = a[ll];
a[ll] = a[hh];
a[hh] = t;
}
ll = hh++;
// recurse on smaller part, loop on larger part
if((ll - lo) <= (hi - hh)){
qsort(a, lo, ll);
lo = hh;
} else {
qsort(a, hh, hi);
hi = ll;
}
}
}
I came across a interview question that has to be done in O(logn)
Given a sorted integer array and a number, find the start and end indexes of the number in the array.
Ex1: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 3 --> Output = {3,6}
Ex2: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 5 --> Output = {-1,-1}
I am trying to find an efficient algo for this but so fat have not been successful.
You can use the concept of binary search to find the starting and ending index:
To find the starting index, you halve the array, if the value is equal to or greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.
To find the starting index, you halve the array, if the value is greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.
Note that when we reached an array of size 1, we may be one cell next to the input number, so we check if it equals the input number, if not, we fix the index by adding/decreasing 1 from the index we found.
findStartIndex(int[] A, int num)
{
int start = 0; end = A.length-1;
while (end != start)
{
mid = (end - start)/2;
if (A[mid] >= num)
end = mid;
else
start = mid;
}
if(A[start] == num)
return start;
else
return start+1;
}
findEndIndex(int[] A, int num)
{
int start = 0; end = A.length-1;
while (end != start)
{
mid = (end - start)/2;
if (A[mid] > num)
end = mid;
else
start = mid;
}
if(A[start] == num)
return start;
else
return start-1;
}
And the whole procedure:
int start = findStartIndex(A, num);
if (A[start]!=num)
{
print("-1,-1");
}
else
{
int end = findEndIndex(A, num);
print(start, end);
}
Sounds like a binary search -- log graphs iirc represent the effect of "halving" with each increment, which basically is binary search.
Pseudocode:
Set number to search for
Get length of array, check if number is at the half point
if the half is > the #, check the half of the bottom half. is <, do the inverse
repeat
if the half point is the #, mark the first time this happens as a variable storing its index
then repeat binary searches above , and then binary searches below (separately), such that you check for how far to the left/right it can repeat.
note*: and you sort binary left/right instead of just incrementally, in case your code is tested in a dataset with like 1,000,000 3's in a row or something
Is this clear enough to go from there?
The solution is to binary search the array concurrently (does't actually have to be concurrent :P ) at the start. The key is that the left and right searches are slightly different. For the right side if you encounter a dupe you have to search to the right, and for the left side if you encounter a dupe you search to the left. what you are searching for is the boundary so on the right side you check for.
yournum, not_yournum
This is the boundary and on the left side you just search for the boundary in the opposite direction. At the end return the indices of the boundaries.
Double binary search. You start with lower index = 0, upper index = length - 1. Then you check the point halfway and adjust your indexes accordingly.
The trick is that once you've found target, the pivot splits in two pivots.
Since no one has posted working code yet, I'll post some (Java):
public class DuplicateNumberRangeFinder {
public static void main(String[] args) {
int[] nums = { 0, 0, 2, 3, 3, 3, 3, 4, 7, 7, 9 };
Range range = findDuplicateNumberRange(nums, 3);
System.out.println(range);
}
public static Range findDuplicateNumberRange(int[] nums, int toFind) {
Range notFound = new Range(-1, -1);
if (nums == null || nums.length == 0) {
return notFound;
}
int startIndex = notFound.startIndex;
int endIndex = notFound.endIndex;
int n = nums.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == toFind && (mid == 0 || nums[mid - 1] < toFind)) {
startIndex = mid;
break;
} else if (nums[mid] < toFind) {
low = mid + 1;
} else if (nums[mid] >= toFind) {
high = mid - 1;
}
}
low = 0;
high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == toFind && (mid == n - 1 || nums[mid + 1] > toFind)) {
endIndex = mid;
break;
} else if (nums[mid] <= toFind) {
low = mid + 1;
} else if (nums[mid] > toFind) {
high = mid - 1;
}
}
return new Range(startIndex, endIndex);
}
private static class Range {
int startIndex;
int endIndex;
public Range(int startIndex, int endIndex) {
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public String toString() {
return "[" + this.startIndex + ", " + this.endIndex + "]";
}
}
}
It may be error on my end, but Ron Teller's answer has an infinite loop when I've tested it. Here's a working example in Java, that can be tested here if you change the searchRange function to not be static.
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class RangeInArray {
// DO NOT MODIFY THE LIST
public static ArrayList<Integer> searchRange(final List<Integer> a, int b) {
ArrayList<Integer> range = new ArrayList<>();
int startIndex = findStartIndex(a, b);
if(a.get(startIndex) != b) {
range.add(-1);
range.add(-1);
return range;
}
range.add(startIndex);
range.add(findEndIndex(a, b));
return range;
}
public static int findStartIndex(List<Integer> a, int b) {
int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;
while(lowerBound < upperBound) {
midIndex = (upperBound + lowerBound) / 2;
if(b <= a.get(midIndex)) upperBound = midIndex - 1;
else lowerBound = midIndex + 1;
}
if(a.get(lowerBound) == b) return lowerBound;
return lowerBound + 1;
}
public static int findEndIndex(List<Integer> a, int b) {
int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;
while(lowerBound < upperBound) {
midIndex = (upperBound + lowerBound) / 2;
if(b < a.get(midIndex)) upperBound = midIndex - 1;
else lowerBound = midIndex + 1;
}
if(a.get(lowerBound) == b) return lowerBound;
return lowerBound - 1;
}
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
list.add(4);
list.add(4);
list.add(4);
list.add(5);
list.add(5);
list.add(5);
System.out.println("Calling search range");
for(int n : searchRange(list, 2)) {
System.out.print(n + " ");
}
}
}
This question already has an answer here:
How can I locate an index given the following constraints? [closed]
(1 answer)
Closed 9 years ago.
Given an array of n integers A[0…n−1], such that ∀i,0≤i≤n, we have that |A[i]−A[i+1]|≤1, and if A[0]=x, A[n−1]=y, we have that x<y. Locate the index j such that A[j]=z, for a given value of z, x≤ z ≤y
I dont understand the problem. I've been stuck on it for 4 days. Any idea of how to approach it with binary search, exponential search or interpolation search recursively? We are given an element z find the index j such that a [j] = z (a j) am i right?.
static int binarySearch(int[] searchArray, int x) {
int start, end, midPt;
start = 0;
end = searchArray.length - 1;
while (start <= end) {
midPt = (start + end) / 2;
if (searchArray[midPt] == x) {
return midPt;
} else if (searchArray[midPt] < x) {
start = midPt + 1;
} else {
end = midPt - 1;
}
}
return -1;
}
You can use the basic binary search algorithm. The fact that A[i] and A[i+1] differ by at most 1 guarantees you will find a match.
Pseudocode:
search(A, z):
start := 0
end := A.length - 1
while start < end:
x = A[start]
y = A[end]
mid := (start+end)/2
if x <= z <= A[mid]:
end := mid
else if A[mid] < z <= y
start := mid + 1
return start
Note that this doesn't necessarily return the first match, but that wasn't required.
to apply your algorithms your need a sorted array.
the condition of you problem says that you have an array which has elements that differ with max 1, not necessarily sorted!!!
so, here are the steps to write the code :
check if problem data respects given conditions
sort input array + saving old indexes values, so later can can initial positions of elements
implement you search methods in recursive way
Binary search source
Interpolation search source
Here's full example source :
public class Test {
// given start ======================================================
public int[] A = new int[] { 1, 1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6,
7, 8 };
public int z = 4;
// given end =======================================================
public int[] indexes = new int[A.length];
public static void main(String[] args) throws Exception {
Test test = new Test();
if (test.z < test.A[0] || test.z > test.A[test.A.length - 1]){
System.out.println("Value z="+test.z+" can't be within given array");
return;
}
sort(test.A, test.indexes);
int index = binSearch(test.A, 0, test.A.length, test.z);
if (index > -1) {
System.out.println("Binary search result index =\t"
+ test.indexes[index]);
}
index = interpolationSearch(test.A, test.z, 0, test.A.length-1);
if (index > -1) {
System.out.println("Binary search result index =\t"
+ test.indexes[index]);
}
}
public static void sort(int[] a, int[] b) {
for (int i = 0; i < a.length; i++)
b[i] = i;
boolean notSorted = true;
while (notSorted) {
notSorted = false;
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
int aux = a[i];
a[i] = a[i + 1];
a[i + 1] = aux;
aux = b[i];
b[i] = b[i + 1];
b[i + 1] = aux;
notSorted = true;
}
}
}
}
public static int binSearch(int[] a, int imin, int imax, int key) {
// test if array is empty
if (imax < imin)
// set is empty, so return value showing not found
return -1;
else {
// calculate midpoint to cut set in half
int imid = (imin + imax) / 2;
// three-way comparison
if (a[imid] > key)
// key is in lower subset
return binSearch(a, imin, imid - 1, key);
else if (a[imid] < key)
// key is in upper subset
return binSearch(a, imid + 1, imax, key);
else
// key has been found
return imid;
}
}
public static int interpolationSearch(int[] sortedArray, int toFind, int low,
int high) {
if (sortedArray[low] == toFind)
return low;
// Returns index of toFind in sortedArray, or -1 if not found
int mid;
if (sortedArray[low] <= toFind && sortedArray[high] >= toFind) {
mid = low + ((toFind - sortedArray[low]) * (high - low))
/ (sortedArray[high] - sortedArray[low]); // out of range is
// possible here
if (sortedArray[mid] < toFind)
low = mid + 1;
else if (sortedArray[mid] > toFind)
// Repetition of the comparison code is forced by syntax
// limitations.
high = mid - 1;
else
return mid;
return interpolationSearch(sortedArray, toFind, low, high);
} else {
return -1;
}
}
}
I don't know why this method is throwing an ArrayIndexOutOfBounds exception.
When I change the initial "high" value to "int high = array.length - 1;", the program will return any integer value that I search for.
What am I doing wrong?
Thanks in advance!
public class BinarySearch {
public static void main(String[] args) {
int searchValue = 12;
int[] givenNums = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
binarySearch(givenNums, searchValue);
System.out.println("\nResult: " + searchValue);
}
public static int binarySearch(int[] array, int key) {
int low = 0;
int high = array.length;
int mid = (low + high) / 2;
int i = 0;
System.out.println();
while (low <= high) {
System.out.print(i + " ");
if (array[mid] < key) {
low = mid + 1;
mid = (low + high) / 2;
} else if (array[mid] > key) {
high = mid - 1;
mid = (low + high) / 2;
}
else
return mid;
i++;
}
return -1;
}
}
You need to be consistent about whether high means the maximum value it can be inclusively or exclusively. You start off with it being an exclusive upper bound:
int high = array.length;
But then your while loop condition is only appropriate if it's an inclusive upper bound:
while (low <= high)
You should probably just change the while condition to:
while (low < high)
... and change the assignment of high later, too.
Alternatively, you could keep it inclusive, and change the initial value to array.length - 1.
That will stop the situation where low == high == mid == array.length, which is where it would blow up.
I'd also suggest moving the mid = (low + high) / 2 computation to be the first statement within the while loop - then you can get rid of the duplicate code.
while (low < high) {
mid = (low + high) / 2;
System.out.print(i + " ");
if (array[mid] < key) {
low = mid + 1;
} else if (array[mid] > key) {
high = mid;
}
else {
return mid;
}
i++;
}
The maximum index of an array is array.length - 1 as they start from 0.
Arrays in java are indexed from 0, that means...
int[] arr = new int[10];
First value is arr[0] and last is arr[9], length is 10.