文章目录
二分法网上有两种写法,一种左闭右闭,一种左闭右开,个人习惯左闭右闭的写法,用二分法主要可以解决有序数组寻找数问题和求有序数组的边界问题:
1. 有序数组寻找数类问题
1.1 找值为target的索引
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size();
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > target)
right = mid - 1;
else if (nums[mid] < target)
left = mid + 1;
else
return mid;
}
return -1;
}
2. 有序数组边界类问题
2.1 求第一个大于target的值的索引(右边界)
找到返回rightBorder,没找到返回-2,如nums = [5,7,7,8,8,10], target = 8
返回第一个大于8的元素10索引,为5
int getRightIndex(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1;
int rightBorder = -2;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > target)
right = mid - 1;
else
{
left = mid + 1;
rightBorder = left;
}
}
return rightBorder;
}
力扣的35. 搜索插入位置也可以用这个思路解决:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int rightBorder = 0;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > target)
right = mid - 1;
else
{
left = mid + 1;
rightBorder = left;
}
}
if (rightBorder == 0)
{
return 0;
}
else
{
if (nums[rightBorder - 1] != target)
return rightBorder;
else
return rightBorder - 1;
}
}
};
当然只有一个target也有更简洁的写法,左闭右闭的写法里最后的left就是右边界了:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > target)
right = mid - 1;
else if (nums[mid] < target)
left = mid + 1;
else
return mid;
}
return left;
}
};
2.2 求最后一个小于target的值的索引(左边界)
找到返回leftBorder,没找到返回-2,如nums = [5,7,7,8,8,10], target = 8
返回第一个小于8的元素7索引,为2
int getLeftIndex(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1;
int leftBorder = -2;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1;
else
{
right = mid - 1;
leftBorder = right;
}
}
return leftBorder;
}
2.3 求第一个大于等于target的值的索引
找到返回left,没找到返回-1,如nums = [5,7,7,8,8,10], target = 8
返回第一个8的索引,为3
int lowerbound(vector<int>& nums, int target)
{
int len = nums.size();
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] >= target)
right = mid - 1;
else
left = mid + 1;
}
if (left < len && nums[left] >= target)
{
return left;
}
else
return -1;
}
2.4 求最后一个小于等于target的值的索引
找到返回left,没找到返回-1,如nums = [5,7,7,8,8,10], target = 8
返回最后一个8的索引,为4
int higherbound(vector<int>& nums, int target)
{
int len = nums.size();
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
if (right >= 0 && nums[right] <= target)
return right;
else
return -1;
}
2.5 求第一个等于target的值的索引
找到返回left,没找到返回-1,如nums = [5,7,7,8,8,10], target = 8
返回第一个8的索引,为3
int lowerbound(vector<int>& nums, int target)
{
int len = nums.size();
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] >= target)
right = mid - 1;
else
left = mid + 1;
}
if (left < len && nums[left] == target)
{
return left;
}
else
return -1;
}
2.6 求最后一个等于target的值的索引
找到返回left,没找到返回-1,如nums = [5,7,7,8,8,10], target = 8
返回最后一个8的索引,为4
int higherbound(vector<int>& nums, int target)
{
int len = nums.size();
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
if (right >= 0 && nums[right] == target)
return right;
else
return -1;
}
3. 典型习题
704.二分查找
标准的二分法
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size();
while (left < right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > target)
right = mid;
else if (nums[mid] < target)
left = mid + 1;
else
return mid;
}
return -1;
}
};
34.在排序数组中查找元素的第一个和最后一个位置。
使用上面找左边界和找右边界的解法,第一个target出现的索引和最后一个target出现的索引,对应的就是左边界+1和右边界-1,题目的完整代码:
class Solution {
public:
int getRightIndex(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1;
int rightBorder = -2;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > target)
right = mid - 1;
else
{
left = mid + 1;
rightBorder = left;
}
}
return rightBorder;
}
int getLeftIndex(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1;
int leftBorder = -2;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1;
else
{
right = mid - 1;
leftBorder = right;
}
}
return leftBorder;
}
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = getLeftIndex(nums, target);
int rightBorder = getRightIndex(nums, target);
if (leftBorder == -2 || rightBorder == -2)
return {-1, -1};
if (rightBorder - leftBorder > 1)
return {leftBorder + 1, rightBorder - 1};
return {-1, -1};
}
};
也可以使用找第一个等于target和最后一个等于target的方法来求解:
class Solution {
public:
int lowerbound(vector<int>& nums, int target)
{
int len = nums.size();
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] >= target)
right = mid - 1;
else
left = mid + 1;
}
if (left < len && nums[left] == target)
{
return left;
}
else
return -1;
}
int higherbound(vector<int>& nums, int target)
{
int len = nums.size();
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
if (right >= 0 && nums[right] == target)
return right;
else
return -1;
}
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = lowerbound(nums, target);
int rightBorder = higherbound(nums, target);
return {leftBorder, rightBorder};
}
};
35.搜索插入位置
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int rightBorder = 0;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > target)
right = mid - 1;
else
{
left = mid + 1;
rightBorder = left;
}
}
if (rightBorder == 0)
{
return 0;
}
else
{
if (nums[rightBorder - 1] != target)
return rightBorder;
else
return rightBorder - 1;
}
}
};
69.x的平方根
class Solution {
public:
int mySqrt(int x) {
if (x == 0 || x == 1)
return x;
int left = 1;
int right = x;
int ans;
while (left <= right)
{
int mid = left + (right - left) / 2;
if ((long long)mid * mid > x)
{
right = mid - 1;
}
else if (mid * mid <= x)
{
left = mid + 1;
ans = left;
}
}
return ans - 1;
}
};
367.有效的完全平方数
class Solution {
public:
bool isPerfectSquare(int num) {
int left = 1;
int right = num;
int ans = 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (mid > INT_MAX / mid || mid * mid > num)
right = mid - 1;
else if (mid * mid <= num)
{
left = mid + 1;
ans = left;
}
}
if ((ans - 1) * (ans - 1) == num)
return true;
return false;
}
};