是什么
概念
二分法(Bisection method) 即一分为二的方法,又叫折半查找方法。
把一组有序数列分为左右两部分,从这组数字的中间位置开始找:
如果中间位置的数等于目标数,则直接返回;
如果中间位置的数大于目标数,则从左边部分查找;
如果小于目标数,则从右边部分查找;
重复以上过程,直到找到满足条件的记录,使查找成功。
场景:从一堆有序的数中,找出一个数;当数据量很大适宜采用该方法。
前提
注意前提是 有序的数列
思想
分治
复杂度
时间复杂度:
都是O(log2 N)空间复杂度:
非递归方式: 空间复杂度是O(1);
递归方式: 空间复杂度:O(log2N )递归需要开辟额外的空间辅助查询。
实现
1. 递归方式
public static int binarySearchRecursive(int[] arr, int low, int high, int key) {
//边界条件:如果目标数没有在数组范围内(即比最左边的数小,比最右边的数大)
if (arr == null || arr.length == 0 || arr[low] > key || arr[high] < key || low > high) {
return -1;
}
// 获取中间位置下标
int mid = (low + high) / 2;
// 将中间位置的数和目标数作比较,如果中间位置的数等于目标数,则直接返回下标,
// 如果中间位置的数大于目标数,则将左边部分用递归方法继续查找;如果小于目标数,则从右边部分用递归方法继续查找
if (arr[mid] == key) {
return mid;
} else if (arr[mid] > key) {
return binarySearch(arr, low, mid - 1, key);
} else {
return binarySearch(arr, mid + 1, high, key);
}
}
// 测试下:从一组数中找3,输出数组下标
public static void main(String[] args) {
int[] arr = {2, 3, 5, 7, 9, 78, 90, 167};
System.out.println(binarySearchRecursive(arr, 0, (arr.length) - 1, 3));
}
结果:
2.非递归方式:while 循环
public static int binarySearch(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int middle = (low + high) / 2;
if (key < arr[middle]) {
high = middle - 1;
} else if (key > arr[middle]) {
low = middle + 1;
} else {
return middle;
}
}
return -1;
}
// 测试下:从一组数中找3,输出数组下标
public static void main(String[] args) {
int[] arr = {2, 3, 5, 7, 9, 78, 90, 167};
System.out.println("数组下标:"+binarySearch(arr, 3));
}
结果:
优缺点
优点: 比较次数少,查找速度快,平均性能好;
缺点:要求待查表为有序表,插入删除困难。
适用于不经常变动而查找频繁的有序列表。
应用
java中java.util包下的Arrays类中的binarySearch 方法:
java.util.Arrays 中的binarySearch
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;
}
return -(low + 1);
}
为什么用 (low + high) >>> 1 代替(low + high) /2
使用(low + high) >>> 1求平均值仅适用于结果保证不是负数的情况,但好处是即使两个数的和int溢出也能求出正确的结果。
用(low + high) >>> 1代替(low + high) /2是非常正确的,首先是因为数组下标肯定不会是负数,另一方面如果low + high大于int最大值时,只有>>>1能保证结果正确。
优化 :
前面 二分法的两种实现 可以优化 为 用“ >>> 1” 右移 代替 " / 2 " .