目录
1.在一个有序数组中,找某个数是否存在
public static boolean exist(int[] sortedArr, int num) {
if (sortedArr == null || sortedArr.length == 0) {
return false;
}//如果数组为空或者数组长度为0,错误
int L = 0;//定义范围的左边界
int R = sortedArr.length - 1;//定义范围的右边界
int mid = 0;
while (L < R) {
mid = L + ((R - L) >> 1);//数组的中间值,这样操作可以防止溢出
if (sortedArr[mid] == num) {
return true;//如果数组的中间值正好等于待查找的数字,直接返回
} else if (sortedArr[mid] > num) {
R = mid - 1;//如果数组的中间值大于待查找的数字,右边界更新为数组的中间值-1
} else {
L = mid + 1;//如果数组的中间值小于待查找的数字,左边界更新为数组的中间值+1
}
}
return sortedArr[L] == num;
}
这个是二分法的基础应用,也叫折半查找,每次砍一半,对算法的时间复杂度进行分析,每次对数组砍一半,最多直到最后只有一个,算法的时间复杂度为,空间复杂度为。
2.在一个有序数组中,找大于等于某个数最左侧的位置
public static int nearestIndex(int[] arr, int value) {
int L = 0;//查找范围的最左侧
int R = arr.length - 1;//查找范围的最右侧
int index = -1;//待查找位置初始化
while (L < R) {
int mid = L + ((R - L) >> 1);//定义中间位置
if (arr[mid] >= value) {//如果中间位置的值大于等于给定数字(在给定值的右侧寻找,不断逼近)
index = mid;//将中间位置赋值给待查找位置
R = mid - 1;//范围的最右侧更新为中间值-1,在左侧查找
} else {
L = mid + 1;//如果中间位置的值小于给定数字,将范围的最左侧更新为中间值+1
}
}//不断循环迭代,直到二分完毕,找到大于等于给定值的最左侧位置,返回
return index;
}
对于这个问题可以利用二分法进行求解,不断二分,缩小范围,到二分完毕时即可找到大于等于给定值最左侧的位置。
3. 在一个数组中,找小于等于某个数最右侧的位置
public static int dichotomyRightIndex(int[] arr,int target) {
//如果数组长度小于1或数组为空,错误
if(arr == null || arr.length <1 ) {
return false;
}
int L = 0;//查找范围的最左侧
int R = arr.length - 1;//查找范围的最右侧
int mid = L + ((R - L) >> 1);
int finalIndex = -1;
while(L <= R) {
if(arr[mid] <= target) { //右侧可能还有答案,继续二分,先记录当前位置(在给定值的左侧寻找,不断逼近)
finalIndex = mid;
L = mid+1;
mid = L + ((R - L) >> 1);
}else { // 答案在左侧
R = mid -1;
mid = L + ((R - L) >> 1);
}
}
return finalIndex;
}
4. 局部最小值问题
局部最小值问题就是对于一个无序的数组,找到局部最小值(局部最小值指的是对于一个数字,如果它比左右两个数字都小,那么它就是局部最小值),找寻局部最小值可以使用二分法进行求解,相对于遍历来说,时间复杂度要小很多。根据数组左右两侧的变化趋势进行查找,不断更新数组两侧的位置,直到找到局部最小值 。
public static int getLessIndex(int[] arr) {
if (arr == null || arr.length == 0) {
return -1;
}//如果数组为空或者长度为0,此时不存在局部最小值,直接返回
if (arr.length == 1 || arr[0] < arr[1]) {
return 0;
}//如果数组的长度为1或者数组的第一个数字小于数组的第二个数字,那么直接返回数组的第一个位置
if (arr[arr.length - 1] < arr[arr.length - 2]) {
return arr.length - 1;//如果数组的最后一个数字小于它的前一个数字,那么最后一个数字就是局部最小值,直接返回它的位置
}
//如果上面的情况均不存在,进行下面的操作,根据增减趋势进行查找
//此时最左侧呈下降趋势,最右侧呈上升趋势,中间一定存在局部最小值
int left = 1;//查找范围的最左侧位置
int right = arr.length - 2;//查找范围的最右侧位置
int mid = 0;//查找范围的中间位置初始化
while (left < right) {
mid = left+((right-left)>>1);//查找范围的中间位置赋值
if (arr[mid] > arr[mid - 1]) {//当数组中间位置的值大于中间位置左侧的值,那么中间位置呈上升趋势,此时右侧范围缩小为中间值-1;
right = mid - 1;
} else if (arr[mid] > arr[mid + 1]) {//当数组中间位置的值大于中间位置右侧的值,那么中间位置呈下降趋势,此时左侧范围缩小为中间值+1
left = mid + 1;
} else {
return mid;//如果中间位置的值同时小于左侧和右侧的值,直接返回中间值就是局部最小值
}
}
return left;//循环结束后返回范围左侧或右侧位置的值为结果
}