03_二分法
二分法
认识二分法
经常见到的类型是在一个有序数组上,开展二分搜索
但有序真的是所有问题求解时使用二分的必要条件吗?
只要能正确构建左右两侧的淘汰逻辑,你就可以二分(排他性)
二分查找
存在性
普通逻辑
public class BSExist {
public static boolean exist(int[] sortedArr, int num) {
if (sortedArr == null || sortedArr.length == 0) return false;
int L = 0, R = sortedArr.length - 1, mid;
while (L <= R) {
mid = L + ((R - L) >> 1);
if (sortedArr[mid] == num) return true;
else if (sortedArr[mid] < num) R = mid - 1;
else L = mid + 1;
}
return false;
}
}
补丁版
public class BSExistAnother {
public static boolean exist(int[] sortedArr, int num) {
if (sortedArr == null || sortedArr.length == 0) return false;
int L = 0, R = sortedArr.length - 1, mid;
while (L < R) {
mid = L + ((R - L) >> 1);
if (sortedArr[mid] < num) L = mid + 1;
else if (sortedArr[mid] > num) R = mid - 1;
else return true;
}
return sortedArr[L] == num;
}
}
不等查找
大于等于某个值最左侧的位置
public class BSNearLeft {
public static int nearestIndex(int[] sortedArr, int value) {
if (sortedArr == null || sortedArr.length == 0) return -1;
int L = 0;
int R = sortedArr.length - 1;
int index = -1;
int mid;
while (L <= R) {
mid = L + ((R - L) >> 1);
if (sortedArr[mid] >= value) {
index = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
return index;
}
}
小于等于某个值最右侧的位置
public class BSNearRight {
public static int nearestIndex(int[] sortedArr, int value) {
if (sortedArr == null || sortedArr.length == 0) return -1;
int L = 0;
int R = sortedArr.length - 1;
int index = -1;
int mid;
while (L <= R) {
mid = L + ((R -L) >> 1);
if (sortedArr[mid] <= value) {
index = mid;
L = mid + 1;
} else {
R = mid - 1;
}
}
return index;
}
}
局部最小值
1.无序数组中任意两个相邻的数都不相等
2.如果边界点比旁边的数小,是局部最小值
public class BSAwesome {
public static int getLessIndex(int[] array) {
if (array == null || array.length == 0) return -1;
// 如果0位置的数是局部最小,则马上返回0位置
// 如果0位置的数不是局部最小,则在0位置的趋势是局部向下的
if (array.length == 1 || array[0] < array[1]) return 0;
// 如果n-1位置是局部最小,则马上返回n-1位置,不用后续过程
// 如果n-1位置不是局部最小,则在n-2到n-1位置的趋势是局部向上的
if (array[array.length - 1] < array[array.length - 2]) return array.length - 1;
// 如果上面两个都不是局部最小,说明整个数组的趋势是先往下再往上的
// 中间必存在一个局部最小
int left = 1;
int right = array.length - 2;
int mid;
while (left < right) {
// 直接来到中点位置
mid = left + ((right - left) >> 1);
// mid有可能比左边大,有可能比右边大,有可能都大
// 设定规则,都大的时候区间往左边缩小
if (array[mid] > array[mid-1]) right = mid - 1;
else if (array[mid] > array[mid + 1]) left = mid + 1;
// 如果mid是局部最小,直接返回
else return mid;
}
return left;
}
}