什么是二分查找算法?
二分查找也称为折半查找,顾名思义,也就是将查找范围缩小一半的查找算法。
那么具体是怎么实现呢?
首先我们要明确一点,二分查找针对的是有序序列的查找。可以是从小到大排序也可以是从大到小排序,本文就以从小到大排序的数组为例子。
我们先确定几个关键的量:
- 首尾元素的下标,既然要将范围缩小,那么首尾下标肯定要知道啊,这样才能求出中间元素的下标。
- 需要查找的变量,当然这和定义的数组必须是相同类型。
- 数组,自定义一个有序数组或者无序数组然后手动将其排序,在其中进行查找。
二分查找的过程是这样的:
假设首元素下标为begin,尾元素下标为end,中间元素下标为mid,查找的元素为num,有序数组为arr。
在满足begin<=end的条件下:
①将num与arr[mid]进行比较,如果num=arr[mid],那么找到该元素,返回下标mid,否则将查找范围缩小一半。
②如果num<arr[mid],进行左半查找,begin不变,end=mid-1,继续从①开始。
③如果num>arr[mid],进行右半查找,end不变,begin=mid+1,继续从①开始。
总结:满足首元素下标小于等于尾元素下标时,一直循环减半查询范围直到找到元素为止;反之,元素不在该数组中。
图解二分查找过程
多说无益,还是看图说话。
定义一个有序数组[11,22,33,44,55,66,77]。
这里将首尾元素下标分别为begin和end,中间元素的下标为(begin+end)/2。
假设我们要查找的元素是11。
初始情况:
进行第一次查找:
此时判断11小于中间元素mid,所以新的查找范围缩小,此时尾部元素的下标变为mid-1。此时新的查找数组即为[11,22,33]。
继续~
进行第二次查找:
此时判断11小于中间元素,查找范围继续缩小,end变为mid-1。
新的查找数组为[11,22]。
继续~
进行第三次查找:
此时begin=0,end=1,mid=(0+1)/2=0,11=mid,所以查找到了11。
如果我们查找的元素为10,那么继续查找~
此时end=mid-1<begin,结束查询,10不在该数组中。
如果我们查找的元素是15,那么继续查找~
此时begin=mid+1=end,查找的数组中只剩22。新的arr[mid]=22,15<arr[mid] 。所以end=mid-1<begin,结束查询,15不在该数组中。
代码实现
说了这么多不如代码说的清楚,上代码!
public class BinarySearch {
public static void main(String[] args) {
System.out.println("请输入需要查找的正整数:");
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
int[] arr = {11,22,33,,44,55,66,77};
binarySearch(0, arr.length-1, num, arr);
}
//定义一个标签判断是否找到元素
public static boolean flag = false;
/**
* 二分查找
* @param num 需要查找的数字
* @param arr 在该数组中查询
* @param begin 开始下标
* @param end 结束下标
*/
public static void binarySearch(int begin,int end,int num,int[] arr){
if (begin <= end){
int mid = (end + begin)/2;
if(num == arr[mid]){
System.out.println("找到了!该数字在数组中的下标为:" + mid);
flag = true;
} else if(num < arr[mid]){
end = mid-1;
binarySearch(begin, end, num, arr);
}else {
begin = mid + 1;
binarySearch(begin, end, num, arr);
}
}
if (!flag){
System.out.println("没有找到该数字!");
}
}
此时虽然已经实现了原来需要的二分查找,但是这里存在一个特殊情况,就是当数组中有多个相同的元素时,会存在查找不全!
那么该如何去处理呢?
很简单,当查找到第一个元素的下标之后,向左右遍历查找即可,直到遍历的元素与需要查找的元素不相等或者下标越界停止。(因为数组有序,所以相同的元素都是相邻的)
改进版代码如下
/**
* 当有多个数字相同时的情况,那么查询的结果就会有多个,必须全部查出来
* @param begin
* @param end
* @param num
* @param arr
*/
public static void binarySearchPlus(int begin,int end,int num,int[] arr){
if (begin <= end){
int mid = (end + begin)/2;
if(num == arr[mid]){
String desc = "找到了!该数字在数组中的下标为:";
int temp = mid - 1;
//向左遍历
while (true){
if (temp < 0 || arr[temp] != num){
break;
}
desc += (" " + temp);
temp--;
}
//将第一个查到的元素加进去
desc += " " + mid;
int temp1 = mid + 1;
//向右遍历
while (true){
if (temp > end || arr[temp1] != num){
break;
}
desc += (" " + temp1);
temp1++;
}
System.out.println(desc);
flag = true;
} else if(num < arr[mid]){
end = mid-1;
binarySearch(begin, end, num, arr);
}else {
begin = mid + 1;
binarySearch(begin, end, num, arr);
}
}
if (!flag){
System.out.println("没有找到该数字!");
}
}