二分查找算法我们会经常对它进行应用以及拓展,但它看起来很简单,实则漏洞状况百出,编写二分查找的算法就很迷惑,云里雾里,不要着急,看完这篇,立马干掉这个难题。
二分查找
1.概念
二分查找是查找算法的一种,它又被称为折半查找,适用于有序数组中的查找。我们在平常进行查找时最通用的方法就是:对数组进行遍历,依次比较每个元素(即顺序查找)。二分查找对其进行优化,将每次的比较查找范围缩小了一半。
2.算法思想
二分查找就是将查找的的元素和子数组的中间值进行比较,如果查找元素小于中间值,就在左子数组中继续查找;如果查找元素大于中间值,就在右子数组中继续查找,否则中间值就是要找的元素。
3.算法步骤
-
首先找到数组查找区间的中间位置
mid=(left+right)>>1
; -
用待查找元素值与中间位置值进行比较;
若=,则查找成功;
若<,则在 左子数组中继续进行查找;
若>,则在 右子数组中继续进行查找; -
查找成功,返回元素所在的数组下标;
4.算法的实现
- 非递归(常用)
//非递归算法
int binarySearch(int[] arr, int target)
{
int left = 0;
int right = arr.length - 1;
while (left <= right)
{
int mid = (left + right) >> 1;
if (target == arr[mid])
return mid;
else if (target < arr[mid])
right = mid - 1;
else if (target > arr[mid])
left = mid + 1;
}
return -1;
}
- 递归(较少应用)
(该数组为升序排列)
int binarySearch_ecursion(int arr[], int target, int left, int right)
{
int mid = 0;
if (left <= right)
{
mid = (left + right) >> 1;
if (target == arr[mid])
return mid;
else if (target < arr[mid])
return binarySearch_ecursion(arr, target, left, mid - 1);
else if (target > arr[mid])
return binarySearch_ecursion(arr, target, mid + 1, right);
}
return -1;
}
5.复杂度分析
- 时间复杂度:O(log2n)
代码编写时的坑
1.定位中间元素
定位中间元素时需要考虑防止溢出的情况,直接将left和right相加数字太大时会导致溢出,mid=(left+right)>>1
和left+(right- left)/2
都可以有效的防止溢出。
2.while循环的条件——(<=)和(<)的选择
首先我们在初始化时right=arr.length - 1
,即是最后一个元素的下标;
其次,什么时候该停止搜索呢,当然是找到目标值的时候停止,如果没有找到,就需要while循环进行终止,然后返回-1;
<=和<两者可能出现在不同功能的二分查找中,<=相当于[left, right]
,<相当于[left, right)
,这样就会导致索引大小出现越界的情况。
解释:
while(left<right)
,的终止条件是[left,right),如果我们待查找的元素为2,遇到[2,2)
,这个时候循环进行终止,而我们的索引2也没有被搜索到直接返回了-1,这样就导致了我们查找出错(漏掉元素)。
3.为什么left=mid+1
,right=mid-1
,怎样判断对其进行加减呢?
这里也是算法中一个难点,但是我们上面分析了搜索区间为[left, right],当索引值和我们元素值不匹配时,下一步如何进行查找呢?
[left,mid-1]
,查找元素小于中间值,就在左子数组中继续查找;[mid+1,right]
,查找元素大于中间值,就在右子数组中继续查找;mid
已经搜索过,所以从区间中剔除;- 其对应代码就为如下:
-
if (target == arr[mid]) return mid; else if (target < arr[mid]) right = mid - 1; else if (target > arr[mid]) left = mid + 1;
这里附上之前在力扣看到某位大佬总结的一首二分要点小诗:
如上,大家是否对二分有了更深刻的认识与了解呢?再也不会因为边界问题而苦恼啦~