二分查找是一种非常经典的查找算法,其应用场景也非常多,算法思路清晰,但是其效率很高。
/** * 二分查找 * 如果数组中存在目标值,返回其对应下标 * 如果数组中不存在目标值,则返回-1 * */ public static int binarySearch(int[] arr,int target){ int low = 0; int high = arr.length-1; while (low<=high){ int mid = low+(high-low)/2; if(target<arr[mid]){ high = mid-1; } else if(target>arr[mid]){ low = mid+1; } else{ return mid; } } return -1; }
注意点:时间复杂度为O(logN),计算mid有两种方式,1) mid=(low+high)/2,2) mid=low+(high-low)/2,low+high有可能出现加法溢出,最好用第二种方式。3) 循环条件有两种方式,low<high和low<=high,如果是low<high,那么相应的high的赋值表达式就是high=mid,如果是low<=high,相应的high的赋值表达式就是high=mid-1。
应用一:求开方
求一个数num的算术平方根sqrt,一个数num的算术平方根sqrt一定在0~num/2之间,并且满足sqrt=num/sqrt,可以利用二分查找在0~num/2之间查找sqrt。
public static int mysqrt(int x){
if (x<=1){
return x;
}
int low=1;
int high=x/2;
while (low<=high){
int mid=low+(high-low)/2;
int sqrt=x/mid;
if (sqrt<mid){
high=mid-1;
}
else if(sqrt>mid){
low=mid+1;
}
else{
return mid;
}
}
return high;
}
应用二:摆硬币
给定n个硬币,在第i行摆i个硬币,求总共可以摆满多少行,可以利用二分查找。
public static int arrangeCoins(int n) { int low = 0; int high = n; while(low <= high){ int mid = low + (high - low) / 2; long x = mid * (mid + 1L) / 2; if(x == n){ return mid; } else if(x < n){ low = mid + 1; } else high = mid - 1; } return high; }
应用三:有序数组的 single element
在一个有序数组中只有一个元素出现一次,其余元素均出现2次,找出这个元素,可以利用二分查找。
public static int singleNoDuplicate(int[] arr){ int low=0; int high=arr.length-1; while (low<high){ int mid = low+(high-low)/2; if (mid%2==1){ mid--; } if(arr[mid]==arr[mid+1]){ low=mid+2; } else{ high=mid; } } return arr[low]; }