二分查找
思想就是我们传统的猜数思想,别人心里想一个数,给你范围,让你进行最少次数的猜测,你肯定先从中间开始猜测,不断折半猜,直到猜到正确的为止。也就是针对一个有序集合,不断缩小区间,直到找到要找的数为止。时间复杂度logn,推理一下:寻找大小为n的数组一个数所需要的区间缩小变化1/2*k=n ,k次缩小,logn=k
适用场景:
(1)适用于顺序表结构,用链表实现也可,但是要多加一个数据域,就是index域,但是时间复杂度特别高,因为每次都要遍历到那个中间结点才行。
(2)数据是有序的
(3)数据量不能过小,不能过大。过小则和顺序查找效率差不多,但如果数据较大,数据比较比较费事,则用二分查找比顺序查找要好。过大由于顺序表的缘故,对连续内存要求,可能会溢出。
package search;
//二分查找
public class BinarySearch {
public static void main(String[] args) {
int[] a= {1,4,5,7,8,9};
System.out.println(binarySearch(a,7));
}
public static int binarySearch(int[] a,int x) {
int low=0,high=a.length-1,middle=-1;
while(low<=high) {
middle=low+((high-low)>>1);
if(a[middle]==x) {
return middle;
}
else if(a[middle]<x) {
low=middle+1;
}
else
high=middle-1;
}
return -1;
}
}
注意:
循环退出条件low<=high;
middle=low+((high-low)>>1),如果用middle=(low+high)/2数据大可能会溢出。移位运算部分要用括号,因为移位运算优先级低于加减法优先级;
low,high不能直接是middle,不然可能死循环
其他变种
package search;
//二分查找的变种 针对有重复数字的数组等
public class StrangeBinarySerach {
public static void main(String[] args) {
int[] a= {1,4,5,7,7,8,9};
System.out.println(firstIndex(a,7));
System.out.println(lastIndex(a,7));
System.out.println(maxEqualIndex(a,7));
System.out.println(minEqualIndex(a,7));
}
//查找第一个等于给定值的数
public static int firstIndex(int[] a,int value) {
int low=0,high=a.length;
while(low<=high) {
int mid=low+((high-low)>>1);
if(a[mid]<value) {
low=mid+1;
}
else if(a[mid]>value) {
high=mid-1;
}
else {
if(mid==0||a[mid-1]!=value)//如果是第一个数,或者当前数前面的数不等于value,证明当前就是要查找的数
return mid;
else
high=mid-1;//否则要找的数一定在low到middle之间
}
}
return -1;
}
//查找最后一个等于给定值的数
public static int lastIndex(int[] a,int value) {
int low=0,high=a.length;
while(low<=high) {
int mid=low+((high-low)>>1);
if(a[mid]<value) {
low=mid+1;
}
else if(a[mid]>value) {
high=mid-1;
}
else {
if(mid==0||a[mid+1]!=value)//如果是第一个数,或者当前数后面的数不等于value,证明当前就是要查找的数
return mid;
else
low=mid+1;//否则要找的数一定在middle到high之间
}
}
return -1;
}
//查找第一个大于等于给定值的数
public static int maxEqualIndex(int[] a,int value) {
int low=0,high=a.length;
while(low<=high) {
int mid=low+((high-low)>>1);
if(a[mid]>=value) {
if(mid==0||a[mid-1]<value)//如果是第一个数,或者当前数前面的数小于value,证明当前就是要查找的数
return mid;
else
high=mid-1;//否则要找的数一定在middle到high之间
}
else {
low=mid+1;
}
}
return -1;
}
//查找最后一个小于等于给定值的数
public static int minEqualIndex(int[] a,int value) {
int low=0,high=a.length;
while(low<=high) {
int mid=low+((high-low)>>1);
if(a[mid]<=value) {
if(mid==0||a[mid+1]>value)//如果是第一个数,或者当前数后面的数大于value,证明当前就是要查找的数
return mid;
else
low=mid+1;//否则要找的数一定在low和middle之间
}
else {
high=mid-1;
}
}
return -1;
}
}