java手撕二分查找在面试中常考,所以抽空在温习一波及几种二分查找常用变形写法(以作记录)。
二分查找也叫折半查找,每一次都与中间数据做比对大小,缩小查找区间的范围。
假设有10个数据,8,11,19,23,27,33,45,55,67,98,利用图分析
小结:
二分查找针对的是一个有序的数据集合也就是数组(这也成为了二分查找的一个重要局限性),查找思想有点类似分治思想。
每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为0
二分查找时间复杂度:O(logn)
简单推导:假设有n个数据,每次查找后数据都会变为n/2,直到查找区间变为空。
也就是 n, n/2 , n/4 , n/8 , n/16 , ···,n/2^k , ···
可以看出来这是一个等比数列。 当 n/2^k = 1时,k就是总共缩小的次数,又因为每一次缩小操作只涉及到两个数据大小比较,所以,经过k次区间变小操作后时间复杂度为O(k). 求得 k = log2n
所以时间复杂度为O(logn)
这是惊人的查找速度,因为logn是一个非常“恐怖”的数量级,即便n非常非常大,对应的logn也很小。比如n等于2的32次方,这个数很大了吧?大约是42亿。也就是说,如果我们在42亿
个数据中用二分查找一个数据,最多需要比较32次
代码:二分查找
public class BinarySearch {
public static void main(String[] args) {
int[] a = {1, 3, 4, 6, 7, 8, 12, 13, 15, 16, 19, 21, 27, 27, 27, 31, 167, 989};
int i = binarySerach(a, 27);
System.out.println("二分查找下标为:" + i);//只要匹配到一个目标值就返回了
int b = binarySerachInterval(a, 0, a.length - 1, 27);
System.out.println("递归方式:" + b);
int c = binarySerachFirstTarget(a, 27);
System.out.println("查找第一个等于目标值下标:"+c);
int d = binarySerachLastTarget(a, 27);
System.out.println("查找最后一个等于目标值下标:"+d);
int e = binarySerachFirstGreater(a, 5);
System.out.println("查找第一个大于目标值的元素下标:"+e);
int f = binarySerachFirstLess(a, 5);
System.out.println("查找最后一个小于目标值的元素下标:"+f);
}
//二分查找底层依赖的是数组,除了数据本身之外,不需要额外的存储其他信息,是最省内存的存储方式
public static int binarySerach(int[] a, int target) {
int low = 0;
int high = a.length - 1;
int mid;
while (low <= high) {
mid = (low + high) >> 1;//这样写是有问题的,如果low或者high比较大的话,两者的和就会溢出,
//改进的写法
// mid = low + (high - low)/2;
if (a[mid] == target) {
return mid;
} else if (a[mid] > target) {
high = mid - 1;
} else if (a[mid] < target) {
low = mid + 1;
}
}
return -1;
}
//递归的方式
public static int binarySerachInterval(int[] a, int low, int high, int target) {
if (low > high) return -1;
int mid = low + ((high - low) >> 1);
if (a[mid] == target) {
return mid;
} else if (a[mid] < target) {
return binarySerachInterval(a, mid + 1, high, target);
} else {
return binarySerachInterval(a, low, mid - 1, target);
}
}
//查找出现的一个目标值的位置
public static int binarySerachFirstTarget(int[] a,int target){
int low = 0;
int high = a.length -1 ;
int mid ;
while (low <= high){
mid = low + ((high - low)>>1);
if (a[mid] < target){
low = mid + 1;
}else if (a[mid] > target){
high = mid - 1;
}else {
//由于数组是有序的
//a[mid-1] 当遍历到a[mid]==target值时候还要去看看 a[mid-1]是否也是==target
//如果a[mid-1]不等于target则可以返回当前下标。
if ((a[mid -1] != target) || (mid == 0)) {
return mid;
}else {
high = mid -1;
}
}
}
return -1;
}
//查找出现的最后一个目标值的位置
public static int binarySerachLastTarget(int[] a,int target){
int low = 0;
int high = a.length -1 ;
int mid ;
while (low <= high) {
mid = low + ((high - low) >> 1);
if (a[mid] < target){
low = mid + 1;
}else if (a[mid] > target){
high = mid - 1;
}else {
if ((a[mid + 1] != target) || (mid == a.length-1)) {
return mid;
}else {
low = mid +1;
}
}
}
return -1;
}
//查找出现的大于一个目标值第一个的位置
public static int binarySerachFirstGreater(int[] a,int target){
int low = 0;
int high = a.length -1 ;
int mid ;
while (low <= high) {
mid = low + ((high - low) >> 1);
if (a[mid] >= target){
if ((mid == 0 ) || (a[mid-1] < target)) return mid;
else high = mid - 1;
}else{
low = mid + 1;
}
}
return -1;
}
public static int binarySerachFirstLess(int[] a,int target){
int low = 0;
int high = a.length -1 ;
int mid ;
while (low <= high) {
mid = low + ((high - low) >> 1);
if (a[mid] > target){
high = mid - 1;
}else{
if ((mid == (a.length-1) ) || (a[mid+1] > target)) return mid;
else low = mid + 1;
}
}
return -1;
}
}
希望面试遇到手撕二分查找能迅速写出来!