一、前提
二分查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
二、算法步骤
置查找区间初值,left为0,right为表长-1。
当left小于等于right时,循环执行以下操作:
1)middle取值为low和high的中间值;
2)将给定值target与中间位置记录的关键字进行比较,
如果target比中间的位置记录的关键字小,则right = middle - 1;
如果target比中间的位置记录的关键字大,则left = middle + 1;
若相等则查找成功,返回中间位置middle。
三、循环版-二分查找
/**
* 循环版-二分查找
* @param array 有序数组
* @param left 左下标
* @param right 右下标
* @param target 要查找的元素
* @return
*/
public static int binarySearch(int[] array,int target){
int left = 0;
int right = array.length - 1;
while (left <= right){
int middle = left + ((right - left) >> 1);
if(array[middle] > target){ //大于
right = middle - 1;
}else if(array[middle] < target){ //小于
left = middle + 1;
}else { //等于(此情况较少出现,所以放在后面)
return middle;
}
}
return -1; //找不到
}
public static void main(String[] args){
int[] arr = {1, 2, 3, 4, 5, 6, 7};
int result = binarySearch(arr,7);
System.out.println(result);
}
四、递归版-二分查找
/**
* 递归版-二分查找
* @param array 有序数组
* param left 左下标
* param right 右下标
* @param target 要查找的元素
* @return
*/
public static int recursiveBSearch(int[] array,int left,int right,int target){
if(left > right){
return -1;
}
//计算中间角标
int middle = left + ((right - left) >> 1);
if(array[middle] > target){//大于
return recursiveBSearch(array,left,middle - 1,target);
}else if(array[middle] < target){//小于
return recursiveBSearch(array,middle + 1,right,target);
}else { //等于
return middle;
}
}
public static void main(String[] args){
int[] arr = {1, 2, 3, 4, 5, 6, 7};
int result = recursiveBSearch(arr,0,arr.length - 1,6);
System.out.println(result);
}
四、时间复杂度
二分查找的时间复杂度无非就是while循环的次数
总共有n个元素,
渐渐跟下去就是n,n/2,n/4,....n/2^k,其中k就是循环的次数
由于你n/2^k取整后>=1
即令n/2^k=1
可得k=log2n,(是以2为底,n的对数)
所以时间复杂度为 O(logn)
六、注意
1.中间值得计算有两种方式
方式一:int middle = (left + right) >> 1;
方式二:int middle = left + ((right - left) >> 1);
方式一存在溢出的风险,当left比right大时,有可能会导致middle的值错误。
方式二则可以保证生成的middle一定大于left,小于right。
2.循环执行的条件是 left <= right,而不是left< right,
因为left = right时,查找区间还有最后一个结点,还要进一步比较。
3.int middle = left + ((right - left) >> 1);中的>> 1为右移一位,相当于除以2。
七、演进版
1、查找第一个等于给定值的元素
/**
* 查找第一个等于给定值的元素
* @param array 有序数组
* @param target 要查找的元素
* @return
*/
public static int firstEqualBSearch(int[] array,int target){
int left = 0;
int right = array.length - 1;
while (left <= right){
int middle = left + ((right - left) >> 1);
if(array[middle] > target){ //大于
right = middle - 1;
}else if(array[middle] < target){ //小于
left = middle + 1;
}else { //等于
//如果当前为第一个元素或者
//当前元素的前一个元素不等target,则返回当前角标
//否则继续查找
if(middle == 0 || array[middle - 1] != target ){
return middle;
}else {
right = middle - 1;
}
}
}
return -1; //找不到
}
2、查找第一个大于给定值的元素
/**
* 查找第一个大于给定值的元素
* @param array 有序数组
* @param target 要查找的元素
* @return
*/
public static int firstGreaterBSearch(int[] array,int target){
int left = 0;
int right = array.length - 1;
while (left <= right){
int middle = left + ((right - left) >> 1);
if(array[middle] > target){ //大于
//如果当前是第一个元素,
//或者当前元素的前一个元素小于等于给定值,则返回
//否则继续查找
if (middle == 0 || array[middle-1] <= target){
return middle;
}else {
right = middle - 1;
}
}else {
left = middle + 1;
}
}
return -1; //找不到
}
3、查找最后一个小于给定值的元素
/**
* 查找最后一个小于给定值的元素
* @param array 有序数组
* @param target 要查找的元素
* @return
*/
public static int firstLessBSearch(int[] array,int target){
int left = 0;
int right = array.length - 1;
while (left <= right){
int middle = left + ((right - left) >> 1);
if(array[middle] < target){ //小于
//如果当前元素的后一个元素大于等于给定值,则返回
//否则继续查找
if (array[middle+1] >= target){
return middle;
}else {
left = middle + 1;
}
}else {
right = middle - 1;
}
}
return -1; //找不到
}