文章目录
- 引言
- 一、二分查找
- 二、查找元素的第一个和最后一个位置
- 三、x的平方根
- 四、搜索插入位置
- 五、山脉数组的峰顶索引
- 六、寻找峰值
- 七、寻找旋转数组中的最小值
- 八、寻找旋转数组中的最小值 ||
- 总结
引言
二分查找,实际上也是左右双指针的变形,本质是利用数组的二段性进行查找。总共有三个模板:朴素二分、左边界二分、右边界二分
一、二分查找
思路:
- 这道题就是标准的朴素二分,也是最简单最基础的二分
- 循环条件:left <= right
- 求中点(两种方法都可以):
- int mid = left + (right-left)/2;//求左中点
- int mid = left + (right-left+1)/2;//求右中点
- 上述求中点方法,可以防止溢出
class Solution
{
public:
int search(vector<int>& nums, int target)
{
int left = 0, right = nums.size() - 1;
while(left <= right)
{
int mid = left + (right-left)/2;
if(nums[mid] < target) left = mid + 1;
else if(nums[mid] > target) right = mid - 1;
else return mid;
}
return -1;
}
};
二、查找元素的第一个和最后一个位置
思路:
- 本题便用到了左边界二分和右边界二分
- 循环条件:left < right(注意:判断等于会死循环)
- 左边界二分:int mid = left + (right-left)/2;//求左中点
- 右边界二分:int mid = left + (right-left+1)/2;//求右中点
- 出循环后,还要再判断当前位置的值是否为target
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
if(nums.size() == 0) return {-1, -1};
int begin = -1, end = -1;
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right-left)/2;
if(nums[mid] >= target) right = mid;
else left = mid + 1;
}
if(nums[left] == target) begin = left;
left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right-left+1)/2;
if(nums[mid] <= target) left = mid;
else right = mid - 1;
}
if(nums[left] == target) end = left;
return {begin, end};
}
};
三、x的平方根
思路:
- 右边界二分
- 注意mid*mid数值太大,int 会溢出,所以改成long long
class Solution
{
public:
int mySqrt(int x)
{
int left = 1, right = x;
while(left < right)
{
long long mid = left + (right - left + 1) / 2;
if(mid * mid <= x) left = mid;
else right = mid - 1;
}
return right;
}
};
四、搜索插入位置
思路:
- 左边界二分
class Solution
{
public:
int searchInsert(vector<int>& nums, int target)
{
if(target > nums.back()) return nums.size();
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right-left)/2;
if(nums[mid] >= target) right = mid;
else left = mid + 1;
}
return left;
}
};
五、山脉数组的峰顶索引
思路:
- 左边界二分(右边界也可以)
class Solution
{
public:
int peakIndexInMountainArray(vector<int>& arr)
{
int left = 0, right = arr.size() - 1;
while(left < right)
{
int mid = left + (right-left)/2;
if(arr[mid] >= arr[mid+1]) right = mid;
else left = mid + 1;
}
return right;
}
};
六、寻找峰值
思路:
- 左边界二分(右边界也可以)
class Solution
{
public:
int findPeakElement(vector<int>& nums)
{
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right-left)/2;
if(nums[mid] >= nums[mid+1]) right = mid;
else left = mid + 1;
}
return left;
}
};
七、寻找旋转数组中的最小值
思路:
- 每次选取right指向的值,作为比较的基准值
- 左边界二分
class Solution
{
public:
int findMin(vector<int>& nums)
{
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right-left)/2;
if(nums[mid] <= nums[right]) right = mid;
else left = mid + 1;
}
return nums[left];
}
};
八、寻找旋转数组中的最小值 ||
思路:
- 这题是上题的变形,主要改变是存在重复元素
- 还是左边界二分,不过在 nums[mid] == nums[right] 时,- -right 即可
class Solution
{
public:
int findMin(vector<int>& nums)
{
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right-left)/2;
if(nums[mid] < nums[right]) right = mid;
else if(nums[mid] == nums[right]) --right;
else left = mid + 1;
}
return nums[left];
}
};
总结
二分查找,是一种十分高效的查找算法,时间复杂度为O(logN),在数组具有二段性时便可应用。
同时,只要掌握并理解了二分的模板,它便是最简单、最容易的一类题型~
版权声明:本文为博主作者:快乐的流畅原创文章,版权归属原作者,如果侵权,请联系我们删除!
原文链接:https://blog.csdn.net/2301_79188764/article/details/137932003