二分查找
二分查找,这题的重点主要在于范围和条件的判断
题目
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
题解A
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0 ;
int right = nums.length - 1 ;
int middle = 0 ;
while(left <= right){
middle = left + (right - left)/2 ;
if(target == nums[middle] ){
return middle ;
}else{
if(target<nums[middle]){
right = middle - 1 ;
}else{
left = middle + 1 ;
}
}
}
return left ;
}
}
显然的二分,return的地方有两个:找到了目标值直接return,找不到就给个应该插入的下标
前者好理解,主要是后者
我们注意到循环的条件是left<=right,也就是我们在搜索完成前,left恒在rigth左侧或二者相等
当出循环的一瞬,left一定是在right右侧的,,而此时也意味着没找到target
临界前最后执行的是
if(target<nums[middle]){
right = middle - 1 ;
}else{
left = middle + 1 ;
}
意味着此时left所在的下标是大等于于middle,大等于right,那么我们就把target插入到这里
以上while循环中,若找到了target直接返回
原数组不包含target时,考虑while循环最后一次执行的总是 left=right=mid,
此时nums[mid] 左边的数全部小于target,nums[mid]右边的数全部大于target,
则此时我们要返回的插入位置分为两种情况:
①就是这个位置,即nums[mid]>target时,此时执行了right=mid-1,返回left正确
②是该位置的右边一个,即nums[mid]<target时,此时执行了left=mid+1,返回left也正确
这个算法在靠近数组边缘的时候不太好理解,下面的解法B会更好理解一点
题解B
public int searchInsert(int[] nums, int target) {
//简单的二分查找
if (nums == null || nums.length == 0) {
return 0;
}
//小知识点: java数组的最大长度为int的最大值
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right) / 2;
if (target == nums[mid]) {
return mid;
} else if (target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
//此时left = right
return target <= nums[left] ? left : left + 1;
}
首先是第一个不同,循环的跳出
他们的更新条件是一样的,但这种做法在left=right的时候就会跳出循环
因此在return的时候额外做了一次判断target是否小于等于nums[left]
如果是,那刚刚好,我们就把目标放在left
如果不是,那意味着我们要把目标放在left的前一位