Arrays 的二分查找

简介: Arrays 的二分查找















  二分查找也称为折半查找,是对有序元素查找的一种算法,在查找的过程中,不断的将搜索长度减半,因此效率不错。Java 的 JDK 提供了二分法查找的算法,使用的方法是Arrays.binarySearch()。binarySearch() 方法提供了多种数据类型的二分查找,比如实现了int、float、double、char、byte 和 Object 类型,还提供了对泛型的支持。在 JavaAPI 手册中提供了接口说明,比如如下方法:

staticintbinarySearch(long[] a, intfromIndex, inttoIndex, longkey)
staticintbinarySearch(long[] a, longkey)
staticintbinarySearch(Object[] a, intfromIndex, inttoIndex, Objectkey)
staticintbinarySearch(Object[] a, Objectkey)
staticintbinarySearch(short[] a, intfromIndex, inttoIndex, shortkey)
staticintbinarySearch(short[] a, shortkey)
static<T>intbinarySearch(T[] a, intfromIndex, inttoIndex, Tkey, Comparator<?superT>c)
static<T>intbinarySearch(T[] a, Tkey, Comparator<?superT>c)

  以上是 Arrays 类中提供的部分关于 binanrySearch() 方法的定义,对于不同类型来说,基本提供了两种方法,第一种方法需要在调用时提供数组、开始下标、结束下标和查找的值,比如:

staticintbinarySearch(long[] a, intfromIndex, inttoIndex, longkey)

  另外一种查找的方法是提供数组和查找的值即可,比如:

staticintbinarySearch(long[] a, longkey)

  对于这两种搜索方法,在 Java 中提供了统一的调用方法,可以查看其代码,在 Java 的安装目录下找到 src.zip 文件,该文件是 Java 的部分源码。将 src.zip 文件解压缩,在java/util/Arrays.java 中可以找到以上两个方法的实现,代码如下:

publicstaticintbinarySearch(long[] a, longkey) {
returnbinarySearch0(a, 0, a.length, key);
}
publicstaticintbinarySearch(long[] a, intfromIndex, inttoIndex,
longkey) {
rangeCheck(a.length, fromIndex, toIndex);
returnbinarySearch0(a, fromIndex, toIndex, key);
}

  从代码中可以看到,两个方法最后都调用了 binarySearch0() 方法,但是在第二个binarySearch() 方法中调用了 rangeCheck() 方法,该方法用于检查数组长度、开始下标和结束下标的正确性,rangeCheck() 方法的实现如下:

/*** Checks that {@code fromIndex} and {@code toIndex} are in* the range and throws an exception if they aren't.*/privatestaticvoidrangeCheck(intarrayLength, intfromIndex, inttoIndex) {
if (fromIndex>toIndex) {
thrownewIllegalArgumentException(
"fromIndex("+fromIndex+") > toIndex("+toIndex+")");
    }
if (fromIndex<0) {
thrownewArrayIndexOutOfBoundsException(fromIndex);
    }
if (toIndex>arrayLength) {
thrownewArrayIndexOutOfBoundsException(toIndex);
    }
}

  如果调用第一个 binarySearch() 方法,数组长度、开始下标和结束下标是方法中自行获取的,因此不需要进行 rangeCheck(),而调用第二个 binarySearch() 方法时,数组长度、开始下标和结束下标是调用时外部提供的,因此为了保证正确性进行了 rangeCheck()。

  二分法真正的实现是 binarySearch0() 方法,根据不同的数据类型,binarySearch0() 方法也提供了多种重载,这里只看 long 类型的实现,代码如下:

privatestaticintbinarySearch0(long[] a, intfromIndex, inttoIndex,
longkey) {
intlow=fromIndex;
inthigh=toIndex-1;
while (low<=high) {
intmid= (low+high) >>>1;
longmidVal=a[mid];
if (midVal<key)
low=mid+1;
elseif (midVal>key)
high=mid-1;
elsereturnmid; // key found    }
return-(low+1);  // key not found.}

  二分查找的思路是从有序(从小到大)数组的中间位置开始查找,如果中间位置的数小于查找的目标值,则查找数组中间值右侧的部分,如果中间位置的数大于查找的目标值,则查找数组中间值左侧的部分,如果相等,则返回当前的下标,如果没有找到则返回一个负数。


  除了上面的实现外,还有一种针对泛型的 binarySeach() 的方法,如下:

static<T>intbinarySearch(T[] a, intfromIndex, inttoIndex, Tkey, Comparator<?superT>c)
static<T>intbinarySearch(T[] a, Tkey, Comparator<?superT>c)

  在上面两个方法的定义中,最后一个参数是一个比较器,比较器的作用是比较两个元素的大小用的,查看以上两个方法的实现,代码如下:

publicstatic<T>intbinarySearch(T[] a, Tkey, Comparator<?superT>c) {
returnbinarySearch0(a, 0, a.length, key, c);
}
publicstatic<T>intbinarySearch(T[] a, intfromIndex, inttoIndex,
Tkey, Comparator<?superT>c) {
rangeCheck(a.length, fromIndex, toIndex);
returnbinarySearch0(a, fromIndex, toIndex, key, c);
}

  以上两个方法同样调用了 binarySearch0() 方法,该 binarySearch0() 方法的实现代码如下:

privatestatic<T>intbinarySearch0(T[] a, intfromIndex, inttoIndex,
Tkey, Comparator<?superT>c) {
if (c==null) {
returnbinarySearch0(a, fromIndex, toIndex, key);
    }
intlow=fromIndex;
inthigh=toIndex-1;
while (low<=high) {
intmid= (low+high) >>>1;
TmidVal=a[mid];
intcmp=c.compare(midVal, key);
if (cmp<0)
low=mid+1;
elseif (cmp>0)
high=mid-1;
elsereturnmid; // key found    }
return-(low+1);  // key not found.}

  观察代码的第 12 行,c 是比较器,该比较器中提供了一个 compare() 方法用来比较两个元素的大小,如果 midVal 比 key 小,compare 返回负数,如果 midVal 比 key 大,compare 返回整数,如果 midVal 和 key 相等,compare 则返回 0。

 

  以上就是在学习 Arrays 工具类的使用时,顺便简单的阅读了它的实现,而刚好又能看懂,所以记录在此!










相关文章
LetCode第912题 排序数组之冒泡排序
依次比较相邻的两du个数,将小数放在前面zhi,大数放在后面。即首先比较第dao1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复以上过程,仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再大于第2个数),将小数放前,大数放后,一直比较到最小数前的一对相邻数,将小数放前,大数放后,第二趟结束,在倒数第二个数中得到一个新的最小数。如此下去,直至最终完成排序。
49 0
LetCode第912题 排序数组之冒泡排序
|
算法 Java
用Java实现冒泡排序和Arrays排序
用Java实现冒泡排序和Arrays排序
75 0
|
算法 搜索推荐 Java
数组的四种排序方法介绍
数组的四种排序方法介绍
293 0
|
Python
LeetCode 349. Intersection of Two Arrays
给定两个数组,编写一个函数来计算它们的交集。
69 0
LeetCode 349. Intersection of Two Arrays
LeetCode 350. Intersection of Two Arrays II
给定两个数组,编写一个函数来计算它们的交集。
75 0
LeetCode 350. Intersection of Two Arrays II
|
搜索推荐 Java
java最简单的三种排序问题(冒泡排序、选择排序、快速排序)
java最简单的三种排序问题(冒泡排序、选择排序、快速排序)
136 0
|
存储 算法
LeetCode 350. 两个数组的交集 II ntersection of Two Arrays II
LeetCode 350. 两个数组的交集 II ntersection of Two Arrays II
|
人工智能
LeetCode之Intersection of Two Arrays
LeetCode之Intersection of Two Arrays
98 0
LeetCode 350: 两个数组的交集 II Intersection of Two Arrays II
题目: 给定两个数组,编写一个函数来计算它们的交集。 Given two arrays, write a function to compute their intersection. 示例 1: 输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2,2] 示例 2: 输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出: [4,9] 说明: 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
720 0