二分法的原理
易错点:(1)左闭右闭 or 左闭右开(2)right=middle or right=middle-1
1.左闭右闭 【left,right】:
left = 0
right = numsize - 1
while(left <= right)
因为left == right是有意义的,所以使用 <=middle = (left + right) / 2
🥇if(num[middle] > target)
nums[middle]一定不等于target 故:right = middle - 1
🥈else if(num[middle]<target)
nums[middle]一定不等于target 故left = middle + 1
🥉else return middle
2.左闭右开 【left,right):
left = 0
right = numsize
while(left < right)
这里使用 < ,因为left == right在区间[left, right)是没有意义的middle = (left + right) / 2
🥇if(num[middle] > target)
此时nums[middle]不含right 故:right = middle
🥈else if(num[middle] < target)
此时nums[middle]等于left 故:left = middle + 1
🥉else return middle
方法:
"闭"的,right = numsize - 1;【右闭】 right or left = middle +- 1
"开"的,right = numsize;【右开】 right or left = middle
【leetcode 35】搜索插入位置


暴力解法
cpp
int searchInsert(vector<int>& nums, int target) {
for(int i = 0; i < nums.size(); i++){
if(nums[i] > =target){
return i;
}
}
return nums.size();
}
二分法
思路:四种情况
①目标值在数组所有元素之前
②目标值等于数组中某一个元素
③目标值插入数组中的位置
④目标值在数组所有元素之后
只要看到面试题里给出的数组是有序数组,都可以想一想是否可以使用二分法。
方法一:左闭右闭 int right = n - 1
cpp
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle;
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1
return right + 1;
}
方法二:左闭右开int right = n
cpp
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n; // 定义target在左闭右开的区间里,[left, right) target
while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle; // target 在左区间,在[left, middle)中
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,在 [middle+1, right)中
} else { // nums[middle] == target
return middle; // 数组中找到目标值的情况,直接返回下标
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0,0)
// 目标值等于数组中某一个元素 return middle
// 目标值插入数组中的位置 [left, right) ,return right 即可
// 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
return right;
}
【leetcode 34】在排序数组中查找元素的第一个和最后一个位置

寻找target在数组里的左右边界,有如下三种情况:
- 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
- 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
- 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
寻找右边界
cpp
// 二分查找,寻找target的右边界(不包括target)
// 如果rightBorder为没有被赋值(即target在数组范围的左边,例如数组[3,3],target为2),为了处理情况一
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) { // 当left==right,区间[left, right]依然有效
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else { // 当nums[middle] == target的时候,更新left,这样才能得到target的右边界
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
寻找左边界
cpp
// 二分查找,寻找target的左边界leftBorder(不包括target)
// 如果leftBorder没有被赋值(即target在数组范围的右边,例如数组[3,3],target为4),为了处理情况一
int getLeftBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] >= target) { // 寻找左边界,就要在nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
处理三种情况
cpp
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// 情况一
if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
// 情况三
if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
// 情况二
return {-1, -1};
}
private:
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle - 1;
} else { // 寻找右边界,nums[middle] == target的时候更新left
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
int getLeftBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
};
tips:画个图!!!
【leetcode 69】x的平方根

cpp
class Solution {
public:
int mySqrt(int x) {
int left = 0;
int right = x;
int ans = -1;
while(left <= right){
int mid = left + (right - left)/2;
if((long long)mid * mid <= x){
ans = mid;
left = mid + 1;
}else{
right = mid -1;
}
}
return ans;
}
};
【leetcode 367】有效的完全平方数

cpp
class Solution {
public:
bool isPerfectSquare(int num) {
int left = 0;
int right = num;
int ans = -1;
while(left <= right){
int mid = left + (right - left)/2;
if((long long)mid * mid < num){
left = mid + 1;
}else if((long long)mid * mid > num){
right = mid - 1;
}else{
return true;
}
}
return false;
}
};