赞
踩
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。时间复杂度为O(logn)。
注意: 二分查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。这是实现二分查找的前提。(排序可以使用sort方法)
public static void main(String[] args) { int[] arr = {10,14,16,25,28,30,35,88,100}; int index1 =binarySearch(arr,100); int index2 =binarySearch(arr,9); int index3 =binarySearch(arr,101); System.out.println("100的索引为:"+index1); System.out.println("9的索引为:"+index2) System.out.println("101的索引为:"+index3); } //二分查找,递归方法 public static int binarySearch(int[] arr,int key){ int low = 0; int high = arr.length-1; while (low <= high){ int mid = (low + high)/2; if (arr[mid] < key){ low = mid+1; }else if (arr[mid] > key){ high = mid -1; }else if (arr[mid] == key) return mid; } return -1; }
运行结果:
100的索引为:8
9的索引为:-1
101的索引为:-1
可以看到在这种迭代的二分查找中,当查找元素在数组中则会返回它对应的下标,当元素不在数组中时,则统统返回-1;
public static void main(String[] args) { int[] arr = {10,14,16,25,28,30,35,88,100}; int index1 =binarySearch1(arr,100,0,arr.length-1); int index2 =binarySearch1(arr,9,0,arr.length-1); int index3 =binarySearch1(arr,101,0,arr.length-1); System.out.println("9的索引为:"+index2); System.out.println("100的索引为:"+index1); System.out.println("101的索引为:"+index3); } //二分查找,递归方法 public static int binarySearch1(int[] arr,int key,int low,int high){ if (low > high) return -1; int mid = (low + high)/2; if (arr[mid] == key) return mid; else if (arr[mid] > key) return binarySearch1(arr,key,low,mid-1); else return binarySearch1(arr,key,mid+1,high); }
运行结果:
100的索引为:8
9的索引为:-1
101的索引为:-1
可以看到在这种递归的二分查找中,其运行结果与上面的迭代是一致的,只是传参时需要在后面加上查找的上界和下界,为它的递归提供条件。也是作为元素不存在的一个标志,即当上界小于下界时,数组内没有此元素,返回-1。
每次对数组进行划分,选取中间的元素,让中间的元素与要查找的元素进行比较,然后不断修改其左右边界。
对于不同的情况,binarySearch方法会做出不同的反应,下面我将其分为两类进行学习:
public static void main(String[] args) {
int[] arr = {1, 10, 23, 35, 55, 66, 88};
//二分查找,binarySearch方法
int index1 = Arrays.binarySearch(arr,66);
int index2 = Arrays.binarySearch(arr,18);
int index3 = Arrays.binarySearch(arr,-1);
int index4 = Arrays.binarySearch(arr,99);
System.out.println("66的索引值为:" + index1);
System.out.println("18的索引值为:" + index2);
System.out.println("-1的索引值为:" + index3);
System.out.println("99的索引值为:" + index4);
}
运行结果:
66的索引为:5
18的索引为:-3
-1的索引为:-1
99的索引为:-8
从返回结果不难看出:
对于key是数组内存在的数,Arrays.binarySearch()方法会返回它对应的下标值。
而对key不是数组内存在的数的情况,分为三类:
那么对于这些不存在数组内的元素,Arrays.binarySearch()方法为什么会分这么多情况,而不是像前两个一样只返回一个 -1呢?我将在后面的源码部分做出解释。
public static void main(String[] args) {
//多个20
int[] arr1 = new int[]{10,20,20,40,50,60};
int index1 = Arrays.binarySearch(arr1,20);
System.out.println("20的下标为:"+index1);
//多个10
int[] arr2 = new int[]{10,10,20,40,50,60};
int index2 = Arrays.binarySearch(arr2,10);
System.out.println("10的下标为:"+index2);
}
运行结果:
20的下标为:2
10的下标为:0
这里也会出现一个神奇的现象,那就是在第一个数组中,返回的是第二个20,而在第一个数组中返回的是第一个10,这又是为什么呢?我也放在下面源码的分析中进行解释。
Arrays.binarySearch()方法的两种传参:
//两个参数,数组a,和要查找的值key
public static int binarySearch(int[] a, int key) {
return binarySearch0(a, 0, a.length, key);
}
//四个参数,在前两个的基础上,增加了两个值表示想要查找的区间段[fromIndex,toIndex]
public static int binarySearch(int[] a, int fromIndex, int toIndex,int key) {
rangeCheck(a.length, fromIndex, toIndex);//此方法是用来检查给出的区间段是否会越界的
return binarySearch0(a, fromIndex, toIndex, key);
}
可以看到这两种传参最后都会去调用binarySearch0这个方法,接下来我们就来看看它的具体实现:
private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) { int low = fromIndex; int high = toIndex - 1; while (low <= high) { int mid = (low + high) >>> 1; int midVal = a[mid]; if (midVal < key) low = mid + 1; else if (midVal > key) high = mid - 1; else return mid; // key found } return -(low + 1); // key not found. }
在这里我们可以看到这就是一个标准的二分查找的流程,但是它在返回上有点不同,当在数组中找不到想找的元素时,它返回的是 - (low + 1) ,并不是 -1,这也是导致对于这些不存在数组内的元素,Arrays.binarySearch()方法为什么会分这么多情况,而不是像前两个方法一样只返回一个 -1的原因。
其实,不仅对于Arrays.binarySearch()方法会产生这样的现象,对于上面的迭代和递归遇到有重复数值的数组也会出现该问题。在Arrays.binarySearch()方法中其实有着一定的规律:
大家也可以尝试下自己写一个函数可以稳定获取重复值中下标小的那一个或者下标更大的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。