文章目录
前言
二分查找法,相信大家都不陌生。是大学的数据结构与算法这门课中,最先接触的几个算法之一。它的实现原理和代码都比较简单。本文用两种方式,递归和非递归的方式,来实现二分查找算法。算法中,对于容易出现溢出的地方,做了改进,希望大家注意。在最后,还留了思考,就是二分查找算法的floor和ceil的实现,有兴趣的可以研究一下。划重点,对于有序序列,才能使用二分查找
。
非递归的二分查找算法
package basic.bst;
/**
* @Description:非递归的二分查找算法
* @Author: during
* @Date: 2020/4/18
*/
public class BinarySearch {
private BinarySearch() {
}
/**
* 二分查找法,在有序数组arr中,查找target
* 如果找到target,返回相应的索引index;如果没有找到target,返回-1
*
* @param arr
* @param target
* @return
*/
public static int find(Comparable[] arr, Comparable target) {
// 在arr[l...r]之中查找target
int l = 0, r = arr.length - 1;
while (l <= r) {
// int mid = (l + r) / 2;
// 防止极端情况下(比如两个都是整型的最大值)的整型溢出,使用下面的逻辑求出mid
int mid = l + (r - l) / 2;
if (arr[mid].compareTo(target) == 0) {
return mid;
} else if (arr[mid].compareTo(target) > 0) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return -1;
}
}
递归的二分查找算法
递归实现通常思维起来更容易,但性能上略差。在后面的测试程序中,对二分查找法的递归和非递归实现也做了相应的对比。
package basic.bst;
/**
* @Description:递归的二分查找算法
* @Author: during
* @Date: 2020/4/18
*/
public class BinarySearchRecursion {
private BinarySearchRecursion() {
}
/**
* 递归的二分查找法:在[l,r]的区间寻找target的值
*
* @param arr
* @param l
* @param r
* @param target
* @return
*/
private static int find(Comparable[] arr, int l, int r, Comparable target) {
if (l > r) {
return -1;
}
// int mid = (l+r)/2;
// 防止极端情况下(比如l和r都为整型的最大值)的整型溢出,使用下面的逻辑求出mid
int mid = l + (r - l) / 2;
if (arr[mid].compareTo(target) == 0) {
return mid;
} else if (arr[mid].compareTo(target) > 0) {
return find(arr, l, mid - 1, target);
} else {
return find(arr, mid + 1, r, target);
}
}
/**
* 二分查找法,在有序数组arr中,查找target
* 如果找到target,返回相应的索引index;如果没有找到target,返回-1
*
* @param arr
* @param target
* @return
*/
public static int find(Comparable[] arr, Comparable target) {
return find(arr, 0, arr.length - 1, target);
}
}
测试二分查找算法
package basic.bst;
/**
* @Description:测试
* @Author: during
* @Date: 2020/4/18
*/
public class Main {
private Main() {
}
public static void main(String[] args) {
int n = 10000000;
Integer[] arr = new Integer[n];
for (int i = 0; i < n; i++) {
arr[i] = new Integer(i);
}
binarySearchTest(n, arr);
binarySearchRecursionTest(n, arr);
}
/**
* 测试非递归二分查找法:非递归算法在性能上有微弱优势
*/
private static void binarySearchTest(int n, Comparable[] arr) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
BinarySearch.find(arr, new Integer(i));
}
long endTime = System.currentTimeMillis();
System.out.println("Binary Search (Without Recursion): " + (endTime - startTime) + "ms");
}
/**
* 测试递归二分查找法
*/
private static void binarySearchRecursionTest(int n, Comparable[] arr) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
BinarySearchRecursion.find(arr, new Integer(i));
}
long endTime = System.currentTimeMillis();
System.out.println("Binary Search (With Recursion): " + (endTime - startTime) + "ms");
}
}
控制台打印结果:
Binary Search (Without Recursion): 1034ms
Binary Search (With Recursion): 1489ms
Process finished with exit code 0
拓展
- floor(lower bound)和ceil(upper bound)
- 上面写的二分查找法,当有重复元素时,不能保证找到的元素的索引是哪个索引。
- 当target有多个,floor能找到target元素第一次出现的位置,ceil能找到target元素最后一次出现的位置。
- 当没有找到target元素的时候,floor返回比target元素小且最近的元素位置,ceil返回比target元素大且最近的元素位置。