二分法
触发关键词:有序数组、查找目标
一、二分法的应用条件
- 有序数组
- 数组内无重复元素
二、二分法的两种常见写法
1.左闭右闭[left,right]
定义target属于[left,right],所以此时
- while (left <= right)
- if (nums[middle] > target)
right = middle-1;
例如在数组:1,2,3,4,7,9,10中查找元素2,如图所示:
代码如下(示例):
class Solution{
public:
int search(vector<int>& num,int target)
{
int left = 0;
int right = nums.size() - 1;// 定义target在左闭右闭的区间里,[left, right]
while(left<=right)// 当left==right,区间[left, right]依然有效,所以用 <=
{
int middle = left + ((right + left)/2);//确定中间值
if (nums[middle] > target)//target在左区间[left,middle]
{
right = middle - 1;
}
else if(nums[middle] < target)//target在右区间[middle,right]
{
left = middle + 1;
}
else //num[middle] == target
{
return middle;
}
}
return -1;//target不在数组num内
}
};
2.左闭右开[left,right)
- while(left < right)
- if(nums[middle] > target)
rigtht = middle;
在数组:1,2,3,4,7,9,10中查找元素2,如图所示:
代码如下(示例):
class Solution{
public:
int search(vector<int>& nums,int target){
int left = 0;
int right = nums.size();// 定义target在左闭右开的区间里,即:[left, right)
while(left < right)
{
int middle = left + (right - left)>>1;//右移运算符>>,运算结果正好能对应一个整数的二分之一值,这就正好能代替数学上的除2运算,但是比除2运算要快。
if(nums[middle]>target)
{
right = middle;
}
else if (nums[middle]<target)
{
left = middle+1;
}
else
return middle;
}
return -1;
}
}
拓展
类似题目练习:
- 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right =nums.size() - 1;
while(left<=right)
{
int middle = left + (right - left)/2;
if(nums[middle] < target)
{
left = middle + 1;
}
else if (nums[middle] > target)
{
right = middle - 1;
}
else
return middle;
}
return right + 1 ;
}
};
- 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
题目分析:
- 情况一:target超出给定数组范围——>此时应该返回{-1,1};
- 情况二:target在数组范围内,但是数组中没有target的值;
- 情况三:target在数组范围内,且数组中有target的值。
为了帮助理解,据几个例子。
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};
else return{-1,-1};
}
int getrightborder(vector<int>& nums,int target)
{
int l = 0;
int r = nums.size() - 1;
int rightborder = -2;
while(l<=r)
{
int m = l + (r - l)/2 ;
if(target < nums[m])
{
r = m - 1;
}
else
{
l = m + 1;
rightborder = l;
}
}
return rightborder;
}
int getleftborder(vector<int>& nums,int target)
{
int l = 0;
int r = nums.size() - 1;
int leftborder = -2;
while(l<=r)
{
int m = l + (r - l)/2;
if(target>nums[m])
{
l = m + 1;
}
else
{
r = m - 1;
leftborder = r;
}
}
return leftborder;
}
};
第二种解法
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int l = left_bound(nums, target);
int r = right_bound(nums, target);
return {l, r};
}
int left_bound(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
// End Condition: left > right
if (left != nums.size() && nums[left] == target) return left;
return -1;
}
int right_bound(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
// End Condition: left > right
if (right != -1 && nums[right] == target) return right;
return -1;
}
};
- 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
在这里插入代码片
- 给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。
不能使用任何内置的库函数,如 sqrt 。
在这里插入代码片