写在前面
二分查找我就不多做介绍,只要是个程序员都知道这个经典的算法。我主要想介绍的是二分查找的实现方式,供大家学习。
总体思路
- 前提必须是有序的数组,如果不是有序数组则不能使用二分查找。我这里举例是升序的数组。降序的数组实现思路相反即可。
- 找中间索引对应的值,然后对比。如果和中间索引的值相等,则返回中间索引的下标;如果小于中间索引值,则在中间索引值左边的区间查找;如果大于中间索引值,则在中间索引值右边的区间查找;
- 如果都没有找到,则返回-1;
使用递归法实现
public static void main(String[] args) throws Exception {
//创建一个从0到一亿的数组
int n = 100000000;
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = i;
}
//数组最后一个索引值
int end = nums.length - 1;
//使用二分查找查找目标值1在数组中的下标
long starTime = System.nanoTime();
int search = new Main().binarySearch(nums, 0, end / 2, end, 1);
long endTime = System.nanoTime();
out.println("耗时:" + (endTime - starTime) / 1000 + "微秒,下标值是:" + search);
}
/*
* 二分查找
*
* @param nums 数组
* @param star 开始索引
* @param mid 中间索引
* @param end 末尾索引
* @param target 目标值
* @return 目标索引的下标;如果没有查询到结果则返回-1
* @date 2020/4/29 11:22
*/
public int binarySearch(int[] nums, int star, int mid, int end, int target) {
//递归终止条件,如果开始索引比末尾索引要大则证明找不到,返回-1
if (star > end) {
return -1;
}
int midVal = nums[mid];
if (midVal == target) {
return mid;
} else if (midVal > target) {
return binarySearch(nums, star, (star + mid - 1) / 2, mid - 1, target);
} else {
return binarySearch(nums, mid + 1, (mid + 1 + end) / 2, end, target);
}
}
耗时结果如下:
//耗时:72微秒,下标值是:1
//耗时:82微秒,下标值是:9
//耗时:86微秒,下标值是:1000
从这里可以看出有一个特点:耗时几乎都差不多。
递归法的缺点
我们都知道如果递归深度太深,会导致栈溢出。有没有不用递归的方式也能实现二分查找的方法呢,答案是肯定有的。
使用迭代法实现
public static void main(String[] args) throws Exception {
//创建一个从0到一亿的数组
int n = 100000000;
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = i;
}
//使用二分查找查找目标值1在数组中的下标
long starTime = System.nanoTime();
int search = new Main().binarySearch(nums, 100);
long endTime = System.nanoTime();
out.println("耗时:" + (endTime - starTime) / 1000 + "微秒,下标值是:" + search);
}
/**
* 二分查找
* @param nums 数组
* @param target 目标值
* @return 目标索引的下标;如果没有查询到结果则返回-1
* @date 2020/4/29 13:51
*/
public int binarySearch(int[] nums, int target) {
//左指针
int left = 0;
//右指针
int right = nums.length - 1;
//中间索引下标
int mid;
//只要左指针小于等于右指针就一直循环
while (left <= right) {
//获取中间索引下标,有些人喜欢写成 left+(right-left)/2;意思是一样的
mid = (left + right) / 2;
int midVal = nums[mid];
//判断中间值与目标值是否相等,相等则找到了,返回下标
if (midVal == target) {
return mid;
}
//如果中间值大于目标值,证明目标值在左边,把右指针移到中间索引前面
else if (midVal > target) {
right = mid - 1;
}
//如果中间值小于目标值,证明目标值在中间值右边,因为是升序排序的数组嘛,把左指针移到中间索引的后面
else {
left = mid + 1;
}
}
//如果左指针大于右指针了,跳出循环,证明找不到目标值,返回-1
return -1;
}
结果也是可以的:
//耗时:65微秒,下标值是:100
//耗时:67微秒,下标值是:200
这样就不担心会出现栈溢出的问题。
总结
二分查找是一种比较经典的算法。通过每次与中间值的对比,然后缩小搜索的范围,以此提高了效率。
如果面试官问了时间复杂度是多少呢?答案是:O(log2n)。
能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!