关于二分查找算法,维基百科是这么说的:
二分查找算法,也称折半搜索算法。是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
几个关键点:
- 前提是查找的序列是有序的。
- 每次搜索过程与搜索区间的中点比较。
- 搜索区间是如何变小的。(变化上界与下界)
- 终止条件是搜索区间为空。
先贴一下该算法的Java实现,最基础的。
要求:在有序序列中查找一个数,查找成功返回下标,查找失败返回-1
。
有两种模板,取决于搜索区间是[l, r]
还是[l, r)
,相应的循环终止的条件也会发生改变。
public int binarySearch(int[] nums, int target) {
int l=0, r=nums.length-1;
while(l<=r) {
int mid=l+(r-l)/2;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
r=mid-1;
else if(nums[mid]<target)
l=mid+1;
}
return -1;
}
public int binarySearch(int[] nums, int target) {
int l=0, r=nums.length; =
while(l<r) { =
int mid=l+(r-l)/2;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
r=mid;
else if(nums[mid]<target)
l=mid+1;
}
return -1;
}
需要注意的是第二种模板由于其循环退出条件为搜索区间为空,左边界等于右边界,其实就相当于锁定了一个元素,我们只需要对这个锁定的元素进行判断就可以了,不用再分别判断l
和r
。常用于查找一个数的左边界或者右边界。
常见的二分查找算法应用场景:
- 查找一个数
- 查找左侧边界
- 查找右侧边界
- 查找失败会有两种情况的返回值:
target
应该插入的位置或者-1
- 查找失败会有两种情况的返回值:
题目要求:查找元素第一次出现的位置,否则返回-1
。
即找到元素的左边界,关键点是1.采用左闭右开的搜索区间使得最后锁定一个元素的位置;2.因为搜索区间右边界无法取到,所以改变上界时取mid
;3.当搜索到与目标值相等的元素后,先不返回,而是将搜索区间右边界继续缩小, 直到锁定到target
第一次出现的位置。
public int binarySearch(int[] nums, int target) {
int l=0, r=nums.length;
while(l<r) {
int mid=l+(r-l)/2;
if(nums[mid]==target)
r=mid;
else if(nums[mid]>target)
r=mid;
else if(nums[mid]<target)
l=mid+1;
}
if(r==nums.length) return -1;
else return (nums[l]==target)? l : -1;
/* 查找失败,要求返回插入位置
if(r==nums.length) return nums.length;
else return l;
*/
}
题目要求:查找元素最后一次出现的位置,否则返回-1
。
需要注意与寻找左边界不同的是我们在这里返回的是l-1
, 因为查找成功的最后一个判断语句是if(nums[mid]==target) l=mid+1;
即最后锁定的元素为查找成功的元素的后一位。
public int binarySearch(int[] nums, int target) {
int l=0, r=nums.length;
while(l<r) {
int mid=l+(r-l)/2;
if(nums[mid]==target)
l=mid+1;
else if(nums[mid]>target)
r=mid;
else if(nums[mid]<target)
l=mid+1;
}
if(l==0) return -1;
else return (nums[l-1]==target)? l-1 : -1;
/* 查找失败,要求返回插入位置
if(l==0) return 0;
else return (nums[l-1]==target)? l-1 : l;
*/
}
对于查找失败返回l
的原因是二分查找不存在的元素,搜索区间会收敛到第一个大于目标的元素的下标。