已阅: 13
算法目录
二分搜索的思想
- 在一个连续的有序数组中,通过比较中间值与目标值的大小,每次缩减一半的搜索范围,逐次缩减后,找到目标值所在位置。
- 例如,对于长度为8的数组nums = {1, 2, 3, 4, 5, 7, 8, 9},需要搜索值为6的元素在数组内的位置:
- 比较中间值nums[ (0 + 7) / 2 ] 与 target值,即nums[3] 与 6,可知nums[3] < 6。
- 左边界设置成之前的中间位置,继续比较中间值nums[ (3 + 7) / 2 ] 与 target值,即nums[5] 与 6,可知nums[5] > 6。
- 右边界设置成之前的中间位置,继续比较中间值nums[ (3 + 5) / 2]与 target值,即nums[4] 与 6,可知nums[4] < 6。
- 左边界设置成之前的中间位置,此时比较范围在nums[4 ~ 5],边界相邻,直接比较nums[4]与6,nums[5]与6,可知两边界均不等于6。
- 返回-1,表示该值不在该数组中。
- 它的时间复杂度为O(logn)。
二分搜索的应用
- 最简单的,在连续有序数组中的非递归二分搜索:LeetCode-简单-35-搜索插入位置(Java)
- 偏移后的连续有序数组中的递归二分搜索:LeetCode-中等-33-搜索旋转排序数组-二分法(C)
- 这个有序数组被偏移了,从12345变成了45123。
- 利用二分搜索寻找连续有序数组中,一连续相同数字段的开头与结尾:LeetCode-中等进阶-34-在排序数组中查找元素的第一个和最后一个位置-二分法(C)
- 要搜索两个值:
- 一个值等于target,且前面的值小于它,
- 第二个值也等于target,但其后面的值大于它。
- 搜索一被无用信息分割开的有序数组中,最后一个大于target值的元素:LeetCode-困难-239-滑动窗口的最大值-队列/二分法(C)
- 有一个队列,占用一定长数组的部分空间,它的队头可能在队尾前面,也可能在队尾后面。
- 它可能长这样:
- 12345----
- 45----123
- --12345--
- 其中,数字1表示队头,数字5表示队尾,-表示该位置不属于队列中元素。
- 在这个队列数组中,需要找到最后一个大于target的值。
二分搜索的格式
- 递归:
int binarySearch(int* nums, int left, int right, int target)
- 其中,nums为待搜索数组,left与right分别为左右边界,target为待搜索的值。
- 进行递归调用时,一定要用return binarySearch()才能正确返回结果。
- 先使用
mid = (left + right) / 2
获得中间位置,方便后续调用。 - 再比较left、right的值:
- 先比较边界可以减少一定计算量:
- 比如一个长度为10000的数组,target值恰巧在左边界上,
- 如果是先比较中间的值,那一般代码就直接将中间值变为右边界继续搜索,左边界的位置不会被比较
- 从nums[0 ~ 9999],nums[0, 4999],nums[0, 2499]一直比较到nums[0, 1]才会因为触发
left + 1 == right
,才会搜索到nums[0]。 - 如果target恰巧在右边界上,同理。
- 再比较
left + 1 == right
:- 如果相同,说明此时比较范围仅剩下左右边界的值,
- 由第3条件可知:左右边界都不等于target,返回-1,表示该值不在nums中。
- 再比较mid的值:
- 如果数组为升序数组,且mid值小于target值,则递归比较nums[mid, right],即
binarySearch(nums, mid, right, target)
- 升序,大于,递归比较nums[left, mid]
- 降序,小于,递归比较nums[left, mid]
- 降序,大于,递归比较nums[mid, right]
- 升序降序和大于小于都挺好判断的,需要用的时候现场判断一下就好,不用记。
- 如果等于就直接返回mid值了。
- 注:
- Leetcode的编译器要求函数必须有返回值,所以最后可以加一个
return -1;
虽然不会被触发,但是不加它无法编译。 - 此外,递归的函数调用时,一定要使用
return binarySearch();
的方法调用,否则出不来正确结果。
- 非递归:
int binarySearch(int* nums, int numsLength, int target)
- 其中,nums是待搜索数组,numsLength是数组长度,target是目标值。
- 非递归和递归不能说一模一样吧,但复制粘贴只要改几个地方是没错的。
- 思想是一样的,就是部分地方改一下就好,可以看下后面代码的区别。
C代码
//测试代码主函数
int main(){
int nums[] = {1, 2, 3, 4, 5, 7, 8, 9};
int target = 999;
printf("%d", binarySearch2(nums, 8, target));
printf("%d", binarySearch(nums, 0, 7, target));
return 0;
}
//递归二分搜索升序数组
int binarySearch(int* nums, int left, int right, int target){
int mid = ( left + right ) / 2;
if(nums[left] == target) return left;
else if(nums[right] == target) return right;
else if(left + 1 == right) return -1;
else if(nums[mid] == target) return mid;
else if(nums[mid] > target) return binarySearch(nums, left, mid, target);
else if(nums[mid] < target) return binarySearch(nums, mid, right, target);
return -1;
}
//非递归二分搜索升序数组
int binarySearch2(int* nums, int numsLength, int target){
int left = 0;
int right = numsLength - 1;
int mid;
while(1){
mid = (left + right) / 2;
if(nums[left] == target) return left;
else if(nums[right] == target) return right;
else if(left + 1 == right) return -1;
else if(nums[mid] == target) return mid;
else if(nums[mid] > target) right = mid;
else if(nums[mid] < target) left = mid;
}
}
Java代码
//测试代码主函数
public static void main(String args[]){
int[] nums = {1, 2, 3, 4, 5, 7, 8, 9};
int target = 9;
System.out.println(binarySearch(nums, 0, 7, target));
System.out.println(binarySearch2(nums, 8, target));
}
//递归二分搜索升序数组
static int binarySearch(int[] nums, int left, int right, int target){
int mid = ( left + right ) / 2;
if(nums[left] == target) return left;
else if(nums[right] == target) return right;
else if(left + 1 == right) return -1;
else if(nums[mid] == target) return mid;
else if(nums[mid] > target) return binarySearch(nums, left, mid, target);
else if(nums[mid] < target) return binarySearch(nums, mid, right, target);
return -1;
}
//非递归二分搜索升序数组
static int binarySearch2(int[] nums, int numsLength, int target){
int left = 0;
int right = numsLength - 1;
int mid;
while(true){
mid = (left + right) / 2;
if(nums[left] == target) return left;
else if(nums[right] == target) return right;
else if(left + 1 == right) return -1;
else if(nums[mid] == target) return mid;
else if(nums[mid] > target) right = mid;
else if(nums[mid] < target) left = mid;
}
}
python代码
#测试函数
nums = [1, 2, 3, 4, 5, 7, 8, 9]
target = 999
print(binarySearch(nums, 0, 7, target))
print(binarySearch2(nums, 8, target))
#递归二分搜索升序数组
def binarySearch(nums, left, right, target):
mid = (left + right) // 2
if nums[left] == target:
return left
elif nums[right] == target:
return right
elif left + 1 == right:
return -1
elif nums[mid] == target:
return mid
elif nums[mid] > target:
return binarySearch(nums, left, mid, target)
elif nums[mid] < target:
return binarySearch(nums, mid, right, target)
return -1
#非递归二分搜索升序数组
def binarySearch2(nums, numsLength, target):
left = 0
right = numsLength - 1
while True :
mid = (left + right) // 2
if nums[left] == target:
return left
elif nums[right] == target:
return right
elif left + 1 == right:
return -1
elif nums[mid] == target:
return mid
elif nums[mid] > target:
right = mid
elif nums[mid] < target:
left = mid
共有 0 条评论