1. 算法解释
二分查找,也称二分法或折半查找。通过将待查找区间分为两部分并都取一部分继续查找,将查找的复杂度大大减少。二分查找适用于对有序或部分有序的数组进行查找。
2. LeetCode 69 求X的平方根 力扣
题目:计算并返回 x 的平方根,其中 x 是非负整数。返回结果向下取整。
思路:如果x>0, x的平方根一定是在[1,x]区间内的,而这个区间是增序的,所以可以对它进行二分查找。写的时候一定要注意x有可能是int的最大取值,做加或乘操作时很可能会溢出。
class Solution {
public:
int mySqrt(int x) {
int l = 1;
int r = x;
if (x < 2) return x;
int mid, sqrt;
while (l <= r) {
mid = l + (r-l)/2; //注意这里不能写成(l+r)/2,因为如果x很大,会溢出。
sqrt = x/mid; //注意这些不能写成mid*mid>x,因为会溢出。
if (mid==sqrt) return mid;
else if (mid > sqrt) {
r = mid-1;
}
else {
l = mid+1;
}
}
return r;
}
};
牛顿迭代法:求方程f(x)=0的解的一种方法。主要原理是:切线是曲线的线性逼近,解释见下:
所以解法二如下:
int mySqrt(int x) {
//将根初始化为x并通过牛顿迭代法不断更新,直到根的平方<=x。
long y = x;
while (y*y>x) y = (y + x/y) / 2;
return y;
}
LeetCode 34. 查找区间 力扣
题目:给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。
思路:该题目可以转换成:求目标值在数组中的结束位置,和“目标值-1”在数组中的结束位置(再+1就是目标值的开始位置)。那么如何求目标值在数组中的结束位置呢?数组本身是有序的,所以使用二分查找。为了保证是最后位置,需要尤其注意细节:1⃣️ 当nums[mid] = target时,要使得low=mid+1; 2⃣️ l初始化为0,r初始化为nums.size()-1,这里为什么是-1?先记住得了。
//辅助函数,寻找数组中某元素的最后位置
int SearchListLast(vector<int> &nums, int target) {
int l = 0, r = nums.size()-1, mid;
while (l<=r) {
mid = l+(r-l)/2;
if (nums[mid] > target) r = mid - 1;
else l = mid + 1;
}
return r;
}
//主函数
vector<int> SearchListRange(vector<int> &nums, int target) {
if (nums.empty()) return vector<int>{-1,-1};
int higher = SearchListLast(nums, target);
int lower = SearchListLast(nums, target-1)+1;
if (lower > nums.size() || higher < 0 || lower > higher)
return vector<int>{-1, -1};
else
return vector<int>{lower, higher};
}
LeetCode81. 搜索旋转排序数组 力扣
题目:已知存在一个按非降序排列的整数数组 nums
,数组中的值不必互不相同。在某个位置将数组切断,然后移动拼接到前面,给你旋转后的数组 nums
和一个整数 target
,请你编写一个函数来判断给定的目标值是否存在于数组中。
思路:1、切段数组的某个部分,肯定还是有序的。对于有序的那部分子数组,仍旧可以使用二分查找法完成搜索。2、如何去找有序的那个子数组?设置中点:mid = (start+end)/2,首先如果mid的值等于target,则返回ture 1⃣️ 如果mid的值等于start值,无法判断左边还是右边都相同,所以让start+1;2⃣️ 否则的话,如果mid的值小于等于end值,则mid右边是有序的;如果mid的值大于end值,则mid左边是有序的。3、找到了那个有序子数组,判断如果target在子数组里,要怎么去更新start或者end的值?
bool search(vector<int>& nums, int target) {
int start = 0, end = nums.size()-1, mid;
while(start <= end){
mid = start + (end - start)/2;
if (nums[mid] == target) return true;
if (nums[mid] == nums[start]) 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;
}