patternjavaModerate
Find the value nearest to k in the sorted array
Viewed 0 times
thearraynearestvaluefindsorted
Problem
Given a sorted array, return the 'closest' element to the input 'x'.
I do understand the merits of unit testing in separate files, but I deliberately added it to the main method for personal convenience, so don't consider that in your feedback.
I'm looking for request code review, optimizations and best practices.
```
public final class ClosestToK {
private ClosestToK() { }
/**
* Given a sorted array returns the 'closest' element to the input 'x'.
* 'closest' is defined as Math.min(Math.abs(x), Math.abs(y))
*
* Expects a sorted array, and if array is not sorted then results are unpredictable.
*
* If two values are equi-distant then the greater value is returned.
*
* @param a The sorted array a
* @return The nearest element
*/
public static int getClosestK(int[] a, int x) {
int low = 0;
int high = a.length - 1;
while (low x) {
return Math.min(Math.abs(a[mid] - x), Math.abs(a[mid + 1] - x)) + x;
}
// keep searching.
if (a[mid] < x) {
low = mid + 1;
} else {
high = mid;
}
}
throw new IllegalArgumentException("The array cannot be empty");
}
public static void main(String[] args) {
// normal case.
int[] a1 = {10, 20, 30, 40};
assertEquals(30, getClosestK(a1, 28));
// equidistant
int[] a2 = {10, 20, 30, 40};
assertEquals(30, getClosestK(a2, 25));
// edge case lower boundary
int[] a3 = {10, 20, 30, 40};
assertEquals(10, getClosestK(a3, 5));
int[] a4 = {10, 20, 30, 40};
assertEquals(10, getClosestK(a4, 10));
// edge case higher boundary
int[] a5 = {10, 20, 30, 40};
assertEquals(40, getClosestK(a5, 45));
int[] a6 = {10, 20, 30, 40};
assertEquals(40, getClosestK(a6, 40));
// case equal to
int[] a
I do understand the merits of unit testing in separate files, but I deliberately added it to the main method for personal convenience, so don't consider that in your feedback.
I'm looking for request code review, optimizations and best practices.
```
public final class ClosestToK {
private ClosestToK() { }
/**
* Given a sorted array returns the 'closest' element to the input 'x'.
* 'closest' is defined as Math.min(Math.abs(x), Math.abs(y))
*
* Expects a sorted array, and if array is not sorted then results are unpredictable.
*
* If two values are equi-distant then the greater value is returned.
*
* @param a The sorted array a
* @return The nearest element
*/
public static int getClosestK(int[] a, int x) {
int low = 0;
int high = a.length - 1;
while (low x) {
return Math.min(Math.abs(a[mid] - x), Math.abs(a[mid + 1] - x)) + x;
}
// keep searching.
if (a[mid] < x) {
low = mid + 1;
} else {
high = mid;
}
}
throw new IllegalArgumentException("The array cannot be empty");
}
public static void main(String[] args) {
// normal case.
int[] a1 = {10, 20, 30, 40};
assertEquals(30, getClosestK(a1, 28));
// equidistant
int[] a2 = {10, 20, 30, 40};
assertEquals(30, getClosestK(a2, 25));
// edge case lower boundary
int[] a3 = {10, 20, 30, 40};
assertEquals(10, getClosestK(a3, 5));
int[] a4 = {10, 20, 30, 40};
assertEquals(10, getClosestK(a4, 10));
// edge case higher boundary
int[] a5 = {10, 20, 30, 40};
assertEquals(40, getClosestK(a5, 45));
int[] a6 = {10, 20, 30, 40};
assertEquals(40, getClosestK(a6, 40));
// case equal to
int[] a
Solution
Here's my solution which seems to work fine :
Here's the principle : the interval
In order to make the range smaller, at each iteration, we'll consider
Edit to answer to comments:
As spotted by @Vick-Chijwani , this code doesn't handle perfectly the scenarios where an element appears multiple times in the input. One can add the following working tests to the code :
but this one will fail :
public static int getClosestK(int[] a, int x) {
int low = 0;
int high = a.length - 1;
if (high < 0)
throw new IllegalArgumentException("The array cannot be empty");
while (low < high) {
int mid = (low + high) / 2;
assert(mid < high);
int d1 = Math.abs(a[mid ] - x);
int d2 = Math.abs(a[mid+1] - x);
if (d2 <= d1)
{
low = mid+1;
}
else
{
high = mid;
}
}
return a[high];
}Here's the principle : the interval
[low, high] will contain the closest element to x at any time. At the beginning, this interval is the whole array ([low, high] = [0, length-1]). At each iteration, we'll make it strictly smaller. When the range is limited to a single element, this is the element we are looking for.In order to make the range smaller, at each iteration, we'll consider
mid the middle point of [low, high]. Because of the way mid is computed, mid+1 is also in the range. We'll check if the closest value is at mid or mid+1 and update high or low accordingly. One can check that the range actually gets smaller.Edit to answer to comments:
As spotted by @Vick-Chijwani , this code doesn't handle perfectly the scenarios where an element appears multiple times in the input. One can add the following working tests to the code :
// case similar elements
int[] a8 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
assertEquals(10, getClosestK(a8, 9));
assertEquals(10, getClosestK(a8, 10));
assertEquals(10, getClosestK(a8, 11));but this one will fail :
int[] a9 = {1, 2, 100, 100, 101};
assertEquals(3, getClosestK(a9, 2)); // actually returns 100Code Snippets
public static int getClosestK(int[] a, int x) {
int low = 0;
int high = a.length - 1;
if (high < 0)
throw new IllegalArgumentException("The array cannot be empty");
while (low < high) {
int mid = (low + high) / 2;
assert(mid < high);
int d1 = Math.abs(a[mid ] - x);
int d2 = Math.abs(a[mid+1] - x);
if (d2 <= d1)
{
low = mid+1;
}
else
{
high = mid;
}
}
return a[high];
}// case similar elements
int[] a8 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
assertEquals(10, getClosestK(a8, 9));
assertEquals(10, getClosestK(a8, 10));
assertEquals(10, getClosestK(a8, 11));int[] a9 = {1, 2, 100, 100, 101};
assertEquals(3, getClosestK(a9, 2)); // actually returns 100Context
StackExchange Code Review Q#47328, answer score: 12
Revisions (0)
No revisions yet.