4、二分算法
主要思想:每次查询都能缩减一半的范围
时间复杂度:O(log n) ; 空间复杂度O(1):
首先的一个问题:是不是只有排好序的元素才能使用二分算法?
下面的第三道算法题会回答这个问题!
1、在一个有序数组中找某个数是否存在
经典二分算法,无需查找至最后一个值。遍历过程中如果发现了target,直接返回。
解题思路:
1、因为是有序数组,直接找中点下标mid
2、若arr[mid] 比我们的目标值大,说明我们的目标值在[0, mid-1]区间
3、若arr[mid] 比我们的目标值小,说明我们的目标值在[mid+1, length]区间
4、每次判断都可将数据范围缩减一半,最终可得结果
public int binarySearch(int[] arr, int target) {
int left = 0,right = arr.length-1;
int result = 0;
while(left < right) {
//这种求中点的方式可以防止整数溢出
int mid = left + ((right - left) >> 1);
if(arr[mid] > target) {
right = mid-1;
continue;
}
if(arr[mid] < target) {
left = mid + 1;
continue;
}
result = mid;
break;
}
return result;
}
2、在一个有序数组中,查找>=某个数的 最左侧的位置
特殊的二分算法,涉及到范围判断,必须查找到最后一个值,才能判断出答案。
解题思路:
1、因为是有序数组,直接找中点下标mid
2、若arr[mid] >= target,说明我们的目标值在[0, mid]区间
3、若arr[mid] < target,说明我们的目标值在[mid+1, length]区间
4、每次判断都可将数据范围缩减一半,最终可得结果
public int rangeBinarySearch(int[] arr, int target) {
int left = 0,right = arr.length-1;
while(left < right) {
int mid = left + ((right-left) >> 1);
if(arr[mid] >= target) {
right = mid;
} else {
left = mid+1;
}
}
return left;
}
3、在一个无序数组中,求一个局部最小值
特殊情况下的二分: 无序
解决算法问题思路:1、数据是否特殊;2、限定条件是否特殊;局部最小定义:i下标的元素,既比i-1下标的元素值小,又比i+1下标的元素值小,则可称之为局部最小;(开头的元素和结尾的元素比较特殊,只有一侧有元素,只需要保证比这一侧的元素小即可)
5 3 3 1 1 6 4 4 8 其中1和4都是局部最小值
解题思路:
1、若两端的元素都不是局部最小值,那么这两个数中间肯定有一个(或多个)低点(局部最小值);
2、取中点,假设中点也不是局部最小值,那么[0, mid) 或者[mid, length] ,这两个区间肯定都有一个或多个低点。
3、假设每次都取[0, mid)
4、每次判断都可将数据范围缩减一半,最终肯定有一次是mid下标即为局部最小值。
public int partMinBinarySearch(int[] arr) {
int left = 0,right = arr.length-1;
//首先判断两个特殊情况
if(arr[left] < arr[left+1]) {
return arr[0];
}
if(arr[right] < arr[right-1]) {
return arr[right];
}
int result = -1;
while(left < right) {
int mid = left + ((right-left) >> 1);
if(isPartMin(arr, mid)) {
result = arr[mid];
break;
} else {
right = mid - 1;
}
}
return result;
}
private boolean isPartMin(int[] arr, int index) {
return arr[index] <= arr[index-1] && arr[index] <= arr[index+1];
}