ArrayIndexOutOfBoundException 5 in Quicksort - java

I'm trying to implement a recursive Quicksort but when I always test it I get an index out of bounds error and I have no idea why :( Tried to debug it by printing some of the array values, and it prints weird stuff. I really have no idea why it prints this, because I checked everything and it normally never should go out of bounds :s
public void testQuicksort){
Integer[] a = {5, 3, 2, 1, -2};
Comparable[] aClone = a.clone();
quickSort.sort(aClone);
public static void sort(Comparable[] a) throws IllegalArgumentException {
if (a == null) {
throw new IllegalArgumentException();
}
sort(a, 0, (a.length - 1));
}
private static void sort(Comparable[] a, int min, int max){
if (max <= min) return;
int right = part(a, min, max);
sort(a, min, right - 1);
sort(a, right + 1, max);
}
private static int part(Comparable[] a, int min, int max){
int left = min, right = max + 1;
Comparable v = a[min];
// printing v prints stuff like this:
"gna.TechnicalTests$IntBox#5dbbdd9b"
while (true) {
// exception in the first while loop
while ((a[++left].compareTo(v) < 0)) if (left == max) break;
while ((v.compareTo(a[--right]) < 0)) if (right == min) break;
if (left >= right) break;
Comparable z = a[left];
a[left] = a[right];
a[right] = z;
}
Comparable y = a[min];
a[min] = a[right];
a[right] = y;
return right;
}

Related

Logical error in a quicksort using a median of 3

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;
}
}
}

Java: How to implement 3 sum?

I'm studying the 3 Sum to implement it on my own, and came across the following implementation with the rules:
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
And implementation (sorts the array, iterates through the list, and uses another two pointers to approach the target):
import java.util.*;
public class ThreeSum {
List<List<Integer>> threeSum(int[] num) {
Arrays.sort(num);
List<List<Integer>> res = new LinkedList<>();
for (int i=0; i<num.length-2; i++) {
if (i==0 || (i>0 && num[i] != num[i-1])) { //HERE
int lo = i+1;
int hi = num.length-1;
int sum = 0 - num[i];
while (lo < hi) {
if (num[lo] + num[hi] == sum) {
res.add(Arrays.asList(num[i], num[lo], num[hi]));
while (lo < hi && num[lo] == num[lo+1]) lo++; //HERE
while (lo < hi && num[hi] == num[hi-1]) hi--; //HERE
lo++; hi--;
} else if (num[lo] + num[hi] < sum) lo++;
else hi--;
}
}
}
return res;
}
//Driver
public static void main(String args[]) {
ThreeSum ts = new ThreeSum();
int[] sum = {-1, 0, 1, 2, -1, -4};
System.out.println(ts.threeSum(sum));
}
}
And my question is (located where commented: //HERE), what's the reason for checking num[i] != num[i-1], num[lo] == num[lo+1], and num[hi] == num[hi-1]? Supposedly they are supposed to skip the same result, but what does that mean? Examples would really help.
Thank you in advance and will accept answer/up vote.
Imagine you have {-1,-1,0,1,2,4} and considering triplet num[0], num[2], num[3] (-1,0,1).
lo=0 here. To exclude triplet num[1], num[2], num[3] with the same values, we should increment lo and pass over duplicate
This will prevent the list to have duplicate triplet.
For example, with you test :
int[] sum = {-1, 0, 1, 2, -1, -4};
will be sorted like :
sum = {-4, -1, -1, 0, 1, 2};
You see that you have -1 twice. Without these test, you would test twice if -1 = 0 + 1. This is not usefull so the algo simply search the next different value.
You could remove duplicate in the sorted List to prevent these test.
Thanks to MBo, we can't remove duplicate since we can have triplet with same value (but with different index)
All the three sentences is used to avoid the duplicate output.
Consider a sorted list {-2, -2 , 1, 1}
If there is no checking for num[i] != num[i-1], the output of the program would be(-2, 1, 1)and(-2, 1, 1), which are two duplicate triplets.
The checking for num[lo] != num[lo + 1]and num[hi] != num[hi - 1] are for the same reason.
Consider a sorted list
{-2,-1,-1,0,3}
If there is no checking for num[lo], you will get (-2,-1,3) and (-2,-1,3) as the output.
Still, I want to recommend a better solution for this problem. You can numerate the sum of two numbers in the list and find the 3rd number by hash or binary search. It will helps you to gain a O(n^2logn) time complexity rather than O(n^3). (I was wrong, the time complexity of this algorithm is O(n^2), sorry for that.)
Following program finds pairs of three integer with O(N*2)
Sort the input Array
and iterate each element in for loop and check for sum in program which is developed for Two sum.
Two sum in linear time after sorting ->
https://stackoverflow.com/a/49650614/4723446
public class ThreeSum {
private static int countThreeSum(int[] numbers) {
int count = 0;
for (int i = 0; i < numbers.length; i++) {
int front = 0, rear = numbers.length - 1;
while (front < rear) {
if (numbers[front] + numbers[rear] + numbers[i] == 0) {
System.out.printf(String.format("Front : {%d} Rear : {%d} I : {%d} \n", numbers[front],
numbers[rear], numbers[i]));
front++;
rear--;
count++;
} else {
if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
front++;
} else {
rear--;
}
}
}
}
return count;
}
public static void main(String[] args) {
int[] numbers = { 1, 3, 5, 7, 12, 16, 19, 15, 11, 8, -1, -3, -7, -8, -11, -17, -15 };
Arrays.sort(numbers);
System.out.println(countThreeSum(numbers));
}
}
It's worked with any NSum (3Sum, 4Sum, 5Sum, ...) and quite fast.
public class ThreeSum {
private static final int RANDOM_RANGE = 20;
private Integer array[];
private Integer arrayIndex[];
private int result[];
private int bagLength;
private int resultIndex = 0;
private void generateData(int size) {
array = new Integer[size];
Random random = new Random();
for (int i = 0; i < size; i++) {
array[i] = random.nextInt(RANDOM_RANGE) - (RANDOM_RANGE/2);
}
}
private void markArrayIndex(int size) {
arrayIndex = new Integer[size];
for (int i = 0; i < size; i++) {
arrayIndex[i] = i;
}
}
private void prepareBeforeCalculate(int size, int sumExpected, int bagLength) {
this.bagLength = bagLength;
result = new int[bagLength];
generateData(size);
markArrayIndex(size);
}
void calculate(int size, int sumExpected, int bagLength) {
prepareBeforeCalculate(size, sumExpected, bagLength);
Arrays.sort(arrayIndex, (l, r) -> array[l].compareTo(array[r]));
System.out.println(Arrays.toString(array));
long startAt = System.currentTimeMillis();
if (sumExpected > 0) findLeft(sumExpected, 0, 0, array.length);
else findRight(sumExpected, 0, 0 - 1, array.length - 1);
System.out.println("Calculating in " + ((System.currentTimeMillis() - startAt) / 1000));
}
private void findLeft(int total, int indexBag, int left, int right) {
while (left < array.length && array[arrayIndex[left]] < 0 && indexBag < bagLength) {
navigating(total, arrayIndex[left], indexBag, left, right);
left++;
}
}
private void findRight(int total, int indexBag, int left, int right) {
while (right >= 0 && array[arrayIndex[right]] >= 0 && indexBag < bagLength) {
navigating(total, arrayIndex[right], indexBag, left, right);
right--;
}
}
private void navigating(int total, int index, int indexBag, int left, int right) {
result[indexBag] = index;
total += array[index];
if (total == 0 && indexBag == bagLength - 1) {
System.out.println(String.format("R[%d] %s", resultIndex++, toResultString()));
return;
}
if (total > 0) findLeft(total, indexBag + 1, left + 1, right);
else findRight(total, indexBag + 1, left, right - 1);
}
private String toResultString() {
int [] copyResult = Arrays.copyOf(result, result.length);
Arrays.sort(copyResult);
int iMax = copyResult.length - 1;
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(array[copyResult[i]]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
}
public class ThreeSumTest {
#Test
public void test() {
ThreeSum test = new ThreeSum();
test.calculate(100, 0, 3);
Assert.assertTrue(true);
}
}

Maximum minus minimum in an array

I am trying to find range(max - min) of an array using recursion.
Since, there can be only one return value, I am kind of confused how to go about this problem.
What I have done so far is to find maximum and minimum recursively and then use this in range function to find the range. I was wondering if it was possible to do everything in just range function somehow recursively.
public static int max(int[] array, int N) {
int maximum;
if (N >= array.length) {
maximum = Integer.MIN_VALUE;
} else {
maximum = max(array, N + 1);
if (array[N] > maximum) {
maximum = array[N];
}
}
return maximum;
}
public static int min(int[] array, int N) {
int minimum;
if (N >= array.length) {
minimum = Integer.MAX_VALUE;
} else {
minimum = min(array, N + 1);
if (array[N] < minimum) {
minimum = array[N];
}
}
return minimum;
}
public static int range(int [] array)
{
int max1 = max(array , 0);
System.out.println(max1);
int min1 = min(array , 0);
System.out.println(min1);
int range = max1 - min1;
return range;
}
If recursion really is a requirement, and you just need the range, then this should do it:
public static int range(int [] array, int index, int min, int max)
{
if (index == array.length) {
if (index == 0)
return 0;
else
return max - min;
}
else {
int value = array[index];
return range(array, index + 1, Math.min(value, min), Math.max(value, max));
}
}
public static int range(int [] array)
{
return range(array, 0, Integer.MAX_VALUE, Integer.MIN_VALUE);
}
Your algorithm seems waaaay too complicated for what you're trying to do.
It's not clear if using recursion is a requirement. If it is not, what about this?
public int range (int[] array) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int elem : array) {
if (elem < min) min = elem;
if (elem > max) max = elem;
}
return (max - min);
}
On mobile so I cannot test any code, but it should work.
EDIT: Ok sorry, re-reading your question I see you just want to know how to do it using recursion. Maybe you'd like to make that clear in the title itself ;-)
You could provide a int[] with min (index 0) and max (index 1) within the method and do all in the same method.
public class Test {
#org.junit.Test
public void test() {
int[] array = new int[] { -5,2,4,6,-10,44};
int[] minmax = new int[ 2 ];
minmax( array, minmax, 0 );
assertEquals( "[-10, 44]", Arrays.toString( minmax ) );
}
public static void minmax(int[] array, int[] minmax, int N) {
if (N >= array.length) {
return;
} else {
minmax(array, minmax, N + 1);
if (array[N] < minmax[0]) {
minmax[0] = array[N];
}
if (array[N] > minmax[1]) {
minmax[1] = array[N];
}
}
}
}
You could for example return an array (0 would have min and 1 have max) that way you can return both values.
A nicer way would be to pass a callback function to you method and call that once done.
findMinMax(array, (min, max) -> { System.out.println(min + " " + max);});
private void findMinMax(int[] array, BiFunction<Integer, Integer, Void> callback) {
//Do things
callback.apply(min, max);
}
This problem doesn't have any improvement from recursion, since it is a iterative problem that - instead - can result in performance issue due to the growing of stack size, in particular for big arrays.
Anyway, a more classical example in java 7 can be the following, where you can use a minimal "Couple" class to store min/max values
public class MaxMinRecurse {
void evalMaxMinInInterval(Couple maxmin, int pos, int[] values) {
if (pos >= values.length)
return;
int x = values[pos];
if (x < maxmin.min) {
maxmin.min = x;
} else if (x > maxmin.max) {
maxmin.max = x;
}
evalMaxMinInInterval(maxmin, pos + 1, values);
}
public static void main(String[] args) {
MaxMinRecurse mmr = new MaxMinRecurse();
int[] values = { 1, 5, 3, 4, 7, 3, 4, 13 };
Couple result = mmr.new Couple();
mmr.evalMaxMinInInterval(result, 0, values);
System.out.println("Max: " + result.max + ", Min:" + result.min);
}
class Couple {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
}
}

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;

Java's equivalent to bisect in python

Is there an equivalent in Java for Python's bisect module? With Python's bisect you can do array bisection with directions. For instance bisect.bisect_left does:
Locate the proper insertion point for item in list to maintain sorted order. The parameters lo and hi may be used to specify a subset of the list which should be considered; by default the entire list is used.
I know I can do this manually with a binary search too, but I was wondering if there is already a library or collection doing this.
You have two options:
java.util.Arrays.binarySearch on arrays
(with various overloads for different array types)
java.util.Collections.binarySearch on List
(with Comparable and Comparator overloads).
Combine with List.subList(int fromIndex, int toIndex) to search portion of a list
To this date (Java 8), this is still missing, so you must still make your own. Here's mine:
public static int bisect_right(int[] A, int x) {
return bisect_right(A, x, 0, A.length);
}
public static int bisect_right(int[] A, int x, int lo, int hi) {
int N = A.length;
if (N == 0) {
return 0;
}
if (x < A[lo]) {
return lo;
}
if (x > A[hi - 1]) {
return hi;
}
for (;;) {
if (lo + 1 == hi) {
return lo + 1;
}
int mi = (hi + lo) / 2;
if (x < A[mi]) {
hi = mi;
} else {
lo = mi;
}
}
}
public static int bisect_left(int[] A, int x) {
return bisect_left(A, x, 0, A.length);
}
public static int bisect_left(int[] A, int x, int lo, int hi) {
int N = A.length;
if (N == 0) {
return 0;
}
if (x < A[lo]) {
return lo;
}
if (x > A[hi - 1]) {
return hi;
}
for (;;) {
if (lo + 1 == hi) {
return x == A[lo] ? lo : (lo + 1);
}
int mi = (hi + lo) / 2;
if (x <= A[mi]) {
hi = mi;
} else {
lo = mi;
}
}
}
Tested with (X being the class where I store static methods that I intend to reuse):
#Test
public void bisect_right() {
System.out.println("bisect_rienter code hereght");
int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
assertEquals(0, X.bisect_right(A, -1));
assertEquals(1, X.bisect_right(A, 0));
assertEquals(6, X.bisect_right(A, 2));
assertEquals(8, X.bisect_right(A, 3));
assertEquals(8, X.bisect_right(A, 4));
assertEquals(9, X.bisect_right(A, 5));
assertEquals(10, X.bisect_right(A, 6));
assertEquals(10, X.bisect_right(A, 7));
}
#Test
public void bisect_left() {
System.out.println("bisect_left");
int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
assertEquals(0, X.bisect_left(A, -1));
assertEquals(0, X.bisect_left(A, 0));
assertEquals(2, X.bisect_left(A, 2));
assertEquals(6, X.bisect_left(A, 3));
assertEquals(8, X.bisect_left(A, 4));
assertEquals(8, X.bisect_left(A, 5));
assertEquals(9, X.bisect_left(A, 6));
assertEquals(10, X.bisect_left(A, 7));
}
Just for completeness, here's a little java function that turns the output from Arrays.binarySearch into something close to the output from bisect_left. I'm obviously missing things, but this does the job for the simple case.
public static int bisectLeft(double[] a, double key) {
int idx = Math.min(a.length, Math.abs(Arrays.binarySearch(a, key)));
while (idx > 0 && a[idx - 1] >= key) idx--;
return idx;
}
Why not do a quick port of the tried and tested Python code itself? For example, here's a Java port for bisect_right:
public static int bisect_right(double[] A, double x) {
return bisect_right(A, x, 0, A.length);
}
private static int bisect_right(double[] A, double x, int lo, int hi) {
while (lo < hi) {
int mid = (lo+hi)/2;
if (x < A[mid]) hi = mid;
else lo = mid+1;
}
return lo;
}
Based on the java.util.Arrays.binarySearch documentation
Here I use the example for a long[] array,
but one can adapt the code to utilize any of the supported types.
int bisectRight(long[] arr, long key) {
int index = Arrays.binarySearch(arr, key);
return Math.abs(index + 1);
}
Note: Limitation on the java API, by the following sentence from javadoc:
If the array contains multiple elements with the specified value,
there is no guarantee which one will be found
Indeed, I've tested that with sorted array of distinct elements.
My use-case was for range grouping, where arr an array of distinct timestamps that indicate the start time of an interval.
You need to define on your own, here's mine:
bisect.bisect_left
public static int bisectLeft(int[] nums, int target) {
int i = 0;
int j = nums.length - 1;
while (i <= j) {
int m = i + (j-i) / 2;
if (nums[m] >= target) {
j = m - 1;
} else {
i = m + 1;
}
}
return i;
}
bisect.bisect_right
public static int bisectRight(int[] nums, int target) {
int i = 0;
int j = nums.length - 1;
while (i <= j) {
int m = i + (j-i) / 2;
if (nums[m] <= target) {
i = m + 1;
} else {
j = m - 1;
}
}
return j+1;
}
Derived from #Profiterole's answer, here is a generalized variant that works with an int->boolean function instead of an array. It finds the first index where the predicate changes.
public class Bisect {
/**
* Look for the last index i in [min, max] such that f(i) is false.
*
* #param function monotonous function going from false to true in the [min, max] interval
*/
public static int bisectLeft(Function<Integer, Boolean> function, int min, int max) {
if (max == min) {
return max;
}
if (function.apply(min)) {
return min;
}
if (!function.apply(max)) {
return max;
}
while (true) {
if (min + 1 == max) {
return min;
}
int middle = (max + min) / 2;
if (function.apply(middle)) {
max = middle;
} else {
min = middle;
}
}
}
/**
* Look for the first index i in [min, max] such that f(i) is true.
*
* #param function monotonous function going from false to true in the [min, max] interval
*/
public static int bisectRight(Function<Integer, Boolean> function, int min, int max) {
if (max == min) {
return max;
}
if (function.apply(min)) {
return min;
}
if (!function.apply(max)) {
return max;
}
while (true) {
if (min + 1 == max) {
return max;
}
int middle = (max + min) / 2;
if (function.apply(middle)) {
max = middle;
} else {
min = middle;
}
}
}
}
For example, to find the insertion point in an array, the function compares the value inserted with the values of the array:
#Test
public void bisect_right() {
int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
assertEquals(0, bisectRight(f(A, -1), 0, A.length));
assertEquals(1, bisectRight(f(A, 0), 0, A.length));
assertEquals(6, bisectRight(f(A, 2), 0, A.length));
assertEquals(8, bisectRight(f(A, 3), 0, A.length));
assertEquals(8, bisectRight(f(A, 4), 0, A.length));
assertEquals(9, bisectRight(f(A, 5), 0, A.length));
assertEquals(10, bisectRight(f(A, 6), 0, A.length));
assertEquals(10, bisectRight(f(A, 7), 0, A.length));
}
public Function<Integer, Boolean> f(int[] A, int x) {
return n -> (n >= A.length || A[n] > x);
}

Categories