基本概念:二分查找又称折半查找,要求待查找数组有序;优点是比较次数少,查找速度快;其缺点是要求待查表为有序表。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
基本思想:首先假设已排序好的序列是升序,将要查找的元素与序列中间的元素比较,若相等,则查找成功;若待查找元素比中间元素大,则查找除去中间元素的后半部分序列,反之,则查找去除中间元素的前半部分序列,直到查到相同的元素或者所查找的序列范围为空为止。
实现思路:还是假设数组元素是升序的,将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的value作比较,如果value=a[n/2]则找到value,算法终止。如 果value<a[n/2],则我们只要在数组a的左半部继续查找value。如果value>a[n/2],则我们只要在数组a的右半部继续查找value。
性能分析:若数组长度为n,则算法复杂度为O(log n)
具体代码:(非递归版)
public class binarySearchTest {
public static int binarySearch(int[] a,int value)
{
//若数组为空,则返回-1
if(a.length == 0 )
{
return -1;
}
int low = 0;
int high = a.length - 1;
while(low <= high)
{
int mid = (low + high) / 2;//这个是有BUG的
//打印中间元素选取过程,只是显示过程,与算法本身无关
for(int i = 0;i < a.length;i++)
{
System.out.print(a[i]);
if(i == mid)
{
System.out.print("#");//用#号表示中间元素
}
System.out.print(" ");//各个元素用空格隔开
}
System.out.println();//每次选取过程换行
//核心
if(value == a[mid])
{
return mid;
}
else if(value > a[mid])
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return -1; //不存在该元素则返回-1
}
public static void main(String[] args) {
int[] a={1,3,4,5,8,7,9,11,15};
System.out.println(binarySearch(a,9));
}
}
具体代码:(递归版)
public class binarySearchRecursionTest {
public static int binarySearchRecursion(int[] a,int fromIndex,int toIndex,int key)
{
if(fromIndex > toIndex) return -1;
int midIndex = (fromIndex + toIndex) / 2;//这个是有BUG的
int midVal = a[midIndex];
if(midVal > key)
{
return binarySearchRecursion(a,fromIndex,midIndex - 1,key);
}
else if(midVal <key)
{
return binarySearchRecursion(a, midIndex + 1, toIndex, key);
}
else
{
return midIndex;
}
}
public static void main(String[] args) {
int[] a={1,3,4,5,8,7,9,11,15};
System.out.println(binarySearchRecursion(a,0,a.length,9));
}
}
代码分析:
早前的jdk中Arrays类中的二分查找存在一个临界值的bug,bug在代码中的int mid = (low + high) / 2;当low + high大于正int范围时就会溢出,会抛出ArrayIndexOutOfBoundsException 异常。
解决办法:将int mid = (low + high) / 2修改为
int mid = low + ((high - low) / 2);
或者 int mid = (low + high) >>> 1;(最新的java.util.Arrays中用的是这个办法)