力扣二分查找(搞不懂)

二分查找也常被称为二分法或者折半查找,每次查找时通过将待查找区间分成两部分并只取一部分继续查找,将查找的复杂度大大减少。对于一个长度为 O(n) 的数组,二分查找的时间复杂度为 O(log n)。
二分查找也可以看作双指针的一种特殊情况,但我们一般会将二者区分。双指针类型的题,指针通常是一步一步移动的,而在二分查找里,指针每次移动半个区间长度。
求开方
给定一个非负整数,求它的开方,向下取整
输入一个整数,输出一个整数
我们可以把这道题想象成,给定一个非负整数 a,求 f (x) = x2 − a = 0 的解。因为我们只考虑 x ≥ 0,所以 f (x) 在定义域上是单调递增的。考虑到 f (0) = −a ≤ 0, f (a) = a2 − a ≥ 0,我们可以对 [0, a] 区间使用二分法找到 f (x) = 0 的解。
我们可以把这道题想象成,给定一个非负整数 a,求 f (x) = x2 − a = 0 的解。因为我们只考虑 x ≥ 0,所以 f (x) 在定义域上是单调递增的。考虑到 f (0) = −a ≤ 0, f (a) = a2 − a ≥ 0,我们可以对 [0, a] 区间使用二分法找到 f (x) = 0 的解。
注意,在以下的代码里,为了防止除以 0,我们把 a = 0 的情况单独考虑,然后对区间 [1, a]进行二分查找。我们使用了左闭右闭的写法

int mySqrt(int a) {
    
    
if (a == 0) return a;
int l = 1, r = a, mid, sqrt;
while (l <= r) {
    
    
mid = l + (r - l) / 2;
sqrt = a / mid;
if (sqrt == mid) {
    
    
return mid;
} else if (mid > sqrt) {
    
    
r = mid - 1;
} else {
    
    
l = mid + 1;
} }
return r;
}

牛顿迭代法
其公式为 xn+1 = xn − f (xn)/ f ′(xn)。给定 f (x) = x2 − a = 0,这里的迭代公式为 xn+1 = (xn + a/xn)/2,其代码如下
这里为了防止 int 超上界,我们使用 long 来存储乘法结果。

int mySqrt(int a) {
    
    
long x = a;
while (x * x > a) {
    
    
x = (x + a / x) / 2;
}
return x;
}

查找区间
给定一个增序的整数数组和一个值,查找该值第一次和最后一次出现的位置。
这道题可以看作是自己实现 C++ 里的 lower_bound 和 upper_bound 函数。这里我们尝试使用左闭右开的写法,当然左闭右闭也可以。
他俩原理就是二分查找
不理解为什么>= 和>
以后再写
在这里插入图片描述
旋转数组查找数字
一个原本增序的数组被首尾相连后按某个位置断开(如 [1,2,2,3,4,5] → [2,3,4,5,1,2],在第一
位和第二位断开),我们称其为旋转数组。给定一个值,判断这个值是否存在于这个为旋转数组
中。
Input: nums = [2,5,6,0,0,1,2], target = 0
Output: true
即使数组被旋转过,我们仍然可以利用这个数组的递增性,使用二分查找。对于当前的中点,
如果它指向的值小于等于右端,那么说明右区间是排好序的;反之,那么说明左区间是排好序的。
如果目标值位于排好序的区间内,我们可以对这个区间继续二分查找;反之,我们对于另一半区
间继续二分查找。
注意,因为数组存在重复数字,如果中点和左端的数字相同,我们并不能确定是左区间全部
相同,还是右区间完全相同。在这种情况下,我们可以简单地将左端点右移一位,然后继续进行
二分查找。

bool search(vector<int>& nums, int target) {
    
    
int start = 0, end = nums.size() - 1;
while (start <= end) {
    
    
int mid = (start + end) / 2;
if (nums[mid] == target) {
    
    
return true; }
if (nums[start] == nums[mid]) {
    
    
// 无法判断哪个区间是增序的
++start;
} else if (nums[mid] <= nums[end]) {
    
    
// 右区间是增序的
if (target > nums[mid] && target <= nums[end]) {
    
    
start = mid + 1;
} else {
    
    
end = mid - 1;
} } else {
    
    
// 左区间是增序的
if (target >= nums[start] && target < nums[mid]) {
    
    
end = mid - 1;
} else {
    
    
start = mid + 1;
} } }
return false; }

おすすめ

転載: blog.csdn.net/qq_45598881/article/details/121057909