详解二分法查找
首先,我们要明白二分法查找适用于一个有序的数组,在java的学习中,我们一般会先接触冒泡排序,使用冒泡排序对无序数组进行排序后再用二分查找去搜索目标数.
二分法查找原理
针对一个有序数组,当我们需要搜索某一个目标数时,我可以一个一个进行遍历搜索,但是,这样会浪费太多计算机资源,并且效率较低,因此,我们需要一个更加高效的方式进行搜索.
二分法查找是指将目标数与有序数组的中间数进行比较,如果中间数大于目标数,则说明目标数在中间数的前面,反之则在中间数后面,再以中间数的前半部(或者后半部)为一个新数组,取中间数与目标数判断,再取新数组再判断,如此循环下去后,直到中间数等于目标数,这样,目标数便找到了.
二分法查找演示
如下图,针对{1,2,3,4,5,6,7,8,9}这样一个数组使用二分法查找8这个元素.
第一次查找:
取{1,2,3,4,5,6,7,8,9}数组的中间数:5,与目标数8进行比较,8>5,说明目标数8位于{6,7,8,9}这个新数组内.
第二次查找:
取{6,7,8,9}这个新数组的中间数:7(注意,由于只有4个数,我们取中间前一位的数作为中间数),与目标数8比较,发现8>7,说明目标数8位于{8,9}这个数组内.
第三次查找:
取{8,9}这个数组的中间数:8,发现目标数8等于中间数,因此找到了.
几个需要关注的地方
- 一个含n个元素的数组(或者说一个长度为n的数组),n为奇数,最多需要((n-1)/2)次查找即可找到目标数,n为偶数最多需要(n/2)次查找即可找到目标数.
- 编写代码时注意循环终止条件为中间数等于目标数.
Java代码
class BinarySearch{
public static void main(String[] args){
//定义1个数组作为目标数组
int[]arr = {1,2,3,11,5,6,7,8,9,10};
//定义1个int变量作为查找的元素
int i = 11;
//调用方法,定义变量接收方法的返回值
int s = binary(MaoP.mao(arr),i);
//打印
System.out.println("你查找的元素索引为:"+s);
}
//定义1个方法,使用二分法查询某个元素的索引
static int binary(int[] arr1,int a){
//定义1个int变量表示二分法中最大索引
int maxIndex = arr1.length - 1;
//定义1个int变量表示二分法中最小索引
int minIndex = 0;
//定义1个int变量表示二分法中的中间索引
int halfIndex = minIndex+(maxIndex-minIndex)/2;
//循环(一般来讲是死循环,但是当查询的元素不位于目标数组中时,会跳出循环)
while(minIndex <= maxIndex){
//如果中间索引位置的元素与目标值相等,则返回索引并退出方法
if (arr1[halfIndex] == a){
return halfIndex;
//如果中间索引位置值小于目标值,则说明被查找元素位于中间索引位置右侧,最小索引等于中间索引加1
}else if(arr1[halfIndex] < a){
minIndex = halfIndex + 1;
//如果中间索引位置值大于目标值,则说明被查找元素位于中间索引位置左侧,最大索引等于中间索引减1
}else{
maxIndex = halfIndex - 1;
}
//未找到,重新给中间索引赋值
halfIndex = minIndex+(maxIndex-minIndex)/2;
}
//未找到返回-1
return -1;
}
}
拓展之二维数组的二分法查询
有兴趣的读者可以看一下,如有错误还请谅解
class TwoDimensionalSearch {
public static void main(String[] args){
//定义需要查询的数
int a = 1;
/*
定义一个二维数组.(每个一维数组的长度相同),
每一行都按照从左到右递增的顺序排序,每一列
都按照从上到下递增的顺序排序
*/
int[][] arr ={{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17,18,19,20},
{21,22,23,24,25}};
//调用方法
Find(a,arr);
}
//定义1个方法查找二维数组中是否存在查询的数
static int Find(int i, int [][] array) {
//定义外层数组的最小,最大,中间索引
int minIndex = 0;
int maxIndex = array.length-1;
int halfIndex = minIndex+(maxIndex-minIndex)/2;
//定义内层数组的最小,最大,中间索引
int min = 0;
int max = array[0].length-1;
int half = min+(max-min)/2;
//外层循环,控制外层数组二分法查询
while(minIndex <= maxIndex){
//内层循环,控制内层数组二分法查询是否有被查询的数
while(min <= max){
//如果有,输出ok,跳出方法,返回0
if (i == array[halfIndex][half]){
System.out.println("查询到该数!");
return 0;
}else if (i > array[halfIndex][half]){
min = half + 1;
}else{
max = half -1;
}
half = min+(max-min)/2;
}
//重新给内层数组的最小,最大,中间索引赋值,使得下次循环内层数组时,各个索引值为初始值
min = 0;
max = array[0].length-1;
half = min+(max-min)/2;
//如果未在当前内层数组查询到该数,则判断该数位于该内层数组后面的数组中
if (i > array[halfIndex][min]){
minIndex = halfIndex + 1;
//如果未在当前内层数组查询到该数,则判断该数位于该内层数组前面的数组中
}else if (i < array[halfIndex][max]){
maxIndex = halfIndex - 1;
}
halfIndex = minIndex+(maxIndex-minIndex)/2;
}
//如果循环到最后都未找到,打印结果
System.out.println("未查询到该数!");
return -1;
}
}