需求:给定一个数组,一个目标值。在数组中查找不大于目标值且下标最大的值、不小于目标值且下标最小的值。
/**
* 二分查找
*/
public class BinarySearch {
/**
* 找出不小于目标值的最小下标
*
* @param array 数组
* @param target 目标值
* @param index 与目标值最接近的值下标
* @return int
*/
public int getLowerBound(int[] array, int target, int index) {
if (array[0] >= target) {
return 0;
}
if (array[array.length - 1] < target) {
return -1;
}
int idx = index;
if (array[index] < target) {
while (array[idx] == array[idx + 1]) {
idx++;
}
idx++;
} else {
while (array[idx] == array[idx - 1]) {
idx--;
}
}
return idx;
}
/**
* 找出不大于目标值的最大下标
*
* @param array 数组
* @param target 目标值
* @param index 与目标值最接近的值下标
* @return int
*/
public int getUpperBound(int[] array, int target, int index) {
if (array[0] > target) {
return -1;
}
if (array[array.length - 1] <= target) {
return index;
}
int idx = index;
if (array[index] > target) {
while (array[idx] == array[idx - 1]) {
idx--;
}
idx--;
} else {
while (array[idx] == array[idx + 1]) {
idx++;
}
}
return idx;
}
/**
* 二分查找与目标值最接近的值
*
* @param array 数组
* @param target 目标值
* @return int
*/
public int search(int[] array, int target) {
if (array.length < 1) {
return -1;
}
if (array.length == 1) {
return 0;
}
int middle = 0;
int low = 0, high = array.length - 1;
if (target <= array[0]) {
return 0;
} else if (target >= array[array.length - 1]) {
return array.length - 1;
}
while (low < high) {
middle = (low + high) / 2;
if (Math.abs(array[middle + 1] - target) > Math.abs(array[middle] - target)) {
high = middle;
} else if (Math.abs(array[middle + 1] - target) < Math.abs(array[middle] - target)) {
low = middle + 1;
} else { // 相等的情况
if (array[middle + 1] >= target) {
high = middle;
} else {
low = middle + 1;
}
}
}
return Math.abs(array[middle + 1] - target) > Math.abs(array[middle] - target) ? middle : middle + 1;
}
@Test
public void test01() {
int[] array = {2, 2, 2, 5, 7, 20, 30, 30, 30, 32, 32, 32, 36, 36, 120, 123, 500, 500, 500};
int val = 30;
int lowerBound = getLowerBound(array, val, search(array, val));
int upperBound = getUpperBound(array, val, search(array, val));
assertEquals(6, lowerBound);
assertEquals(8, upperBound);
val = 32;
lowerBound = getLowerBound(array, val, search(array, val));
upperBound = getUpperBound(array, val, search(array, val));
assertEquals(9, lowerBound);
assertEquals(11, upperBound);
val = 1;
lowerBound = getLowerBound(array, val, search(array, val));
upperBound = getUpperBound(array, val, search(array, val));
assertEquals(0, lowerBound);
assertEquals(-1, upperBound);
val = 510;
lowerBound = getLowerBound(array, val, search(array, val));
upperBound = getUpperBound(array, val, search(array, val));
assertEquals(-1, lowerBound);
assertEquals(18, upperBound);
val = 120;
lowerBound = getLowerBound(array, val, search(array, val));
upperBound = getUpperBound(array, val, search(array, val));
assertEquals(14, lowerBound);
assertEquals(14, upperBound);
val = 2;
lowerBound = getLowerBound(array, val, search(array, val));
upperBound = getUpperBound(array, val, search(array, val));
assertEquals(0, lowerBound);
assertEquals(2, upperBound);
val = 500;
lowerBound = getLowerBound(array, val, search(array, val));
upperBound = getUpperBound(array, val, search(array, val));
assertEquals(16, lowerBound);
assertEquals(18, upperBound);
int[] vals = {30, 32, 1, 510, 120 , 2, 500};
int[] expectLowBound = {6, 9, 0, -1, 14, 0, 16};
int[] expectUpperBound = {8, 11, -1, 18, 14, 2, 18};
for (int i = 0; i < vals.length; i++) {
assertEquals(expectLowBound[i], getLowerBound(array, vals[i], search(array, vals[i])));
assertEquals(expectUpperBound[i], getUpperBound(array, vals[i], search(array, vals[i])));
}
}