0.最原始的二分查找
本文勉强算是递归系列的第八篇文章吧。不过实际上本文是二分专题。以多道LC上题介绍二分的解题思路与模板。
沿着递归的思路,今天我们来谈谈二分查找
严格来说,二分查找和递归联系不算太紧密,本文介绍的主要是非递归版本的二分查找类型习题的解析,话不多说,先来看看原始的BinarySearch吧!
给定一个没有重复元素的有序数组arr,查找目标值target的下标,若arr中没有则返回-1
由于是递归系列,那我们从递归版的二分开始:
(ps:本文代码中中部分边界判空条件省略)
/* 递归版二分查找 */
public static int binarySearchRecur(int[] arr ,int target, int left , int right){
if(left >= right) return -1;
int mid = (right + left) >> 1;
if(arr[mid] == target )
return mid;
if(arr[mid] > target)
return binarySearchRecur(arr, target , left , mid -1);
if(arr[mid] < target)
return binarySearchRecur(arr,target , mid + 1, right);
return -1;
}
下面改成非递归版的,也是我们今天得重头戏
/*
迭代版二分查找 LeetCode704
*/
public static int binarySearchIter(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if(arr[mid] == target)
return mid;
if(arr[mid] > target)
right = mid - 1;
if(arr[mid] < target)
left = mid + 1;
}
return -1;
}
1.寻找第一个/最后一个出现target的位置
给定一个可能有重复元素的有序数组arr,查找目标值target第一次出现的下标,若arr中没有则返回-1
比如:[1,3,4,5,5,5,5,7] ,target = 5
返回 :3
[2,5,6,6,6,8],target = 7
返回:-1
给定一个可能有重复元素的有序数组arr,查找目标值target最后一次出现的下标,若arr中没有则返回-1
比如:[1,3,4,5,5,5,5,7] ,target = 5
返回 :6
这是二分中很经典的一类问题,求区间。
第一个题目问法有几种形式:
- 求第一个出现target的下标
- 求小于target的个数
- 求小于target的最大元素
两种种说法做法几乎的!!!其中第1和第2点完全相同,只是换了种说法,第3点在1,2结果减1即可
1.1求第一个出现target的下标 / 小于target的个数
在基本二分的框架中稍作修改:
/* 求第一个出现target的下标 / 小于target的个数 */
public static int findFirstTarget(int[] arr , int target){
int left = 0 , right = arr.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
if(arr[mid] == target)
right = mid - 1; //注意1
if(arr[mid] > target)
right = mid - 1;
if(arr[mid] < target)
left = mid + 1;
}
if(left >= arr.length ||arr[left] != target) //注意2
return -1;
return left;
}
与上面纯正二分相比,注意代码中的两个地方:
-
当
arr[mid] == target
时,并不是直接返回mid,为啥?因为这时并不知道mid所在位置是否为target的第一个(他的左边可能还有target)。因此将右边界缩小,往左边找。
-
while结束时,left停留在righ+1的位置。
若:right始终在最右边,则
left=arr.length
,不存在该下标(target比所有元素都大,此时未找到target)若:left最终停留在中间位置,但
arr[left] != target
,(此时找到的只是合适的插入target的位置,同样未找到target)
1.2求最后一个出现target的下标
和上面类似,直接上代码:
/*
最后出现target的位置
*/
public static int findLastTarget(int[] arr , int target){
int left = 0 , right = arr.length -1;
while (left <= right){
int mid = left + (