二分查找
https://leetcode.cn/problems/binary-search/
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
思路:简单的二分查找,通过left和right两侧的值求中间值,判断中间值和target的大小比较。
注意点有两个,我本人喜欢采用左闭右闭的写作方法:
left = 0;right = nums.size() - 1;
当target和mid不相等的时候,让left = mid+1,right= mid - 1。因为mid肯定不等于target,所以left等于mid的话也肯定不会等于target所以让left + 1,right同理。
代码如下:
class Solution {
public:
int search(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;
}
if( nums[mid] < target){
left = mid + 1;
}
if( nums[mid] == target){
return mid;
}
}return -1;
}
};
搜索插入位置
https://leetcode.cn/problems/search-insert-position/
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
思路:发现数组有序排列就可以考虑二分法,此题直接用二分法可以解决,相比上一个题目有一个特殊的地方在于返回应该插入的位置,这个可以这样思考,当while遍历完后,也就是left和right相等后下一次便利,left变到了right的右边,这个时候结束循环,而我们要插入的值正好在right的右边一个。
代码如下:
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){
return mid;
}
if(nums[mid] > target){
right = mid - 1;
}
if(nums[mid] < target){
left = mid + 1;
}
}
return right + 1;
}
};
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;
}
};
在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
思路1:这道题有两种解法,一种是暴力,也就是从前往后遍历,如果碰到了target就给a赋值,如果a不是初始值,也就是说碰到了下一个target,这个时候给b赋值即可。
代码如下:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int a=-1,b=-1;int n=nums.size();int flag=0;
for(int i=0;i<n;i++){
if(nums[i]==target){
flag++;
if(flag==1)a=i;
if(flag>=2)b=i;
}
}
if(flag==1)return {a,a};
return {a,b};
}
};
思路2:思路1的方法虽然可以过,但是运算速度太慢,当我们遇到需要在数组中寻找或者填补的时候,第一时间想到二分法。这里的二分法稍微有点区别,那就是这里的二分法寻找左右区间:
首先我们想有几种情况:
情况一:target不在数组包含的数值之内,也就是target在数组左或右。
情况二:target在数组包含的数值之内,但是数组里没有target。
情况三:target在数组包含的数值之内,同时数组里面有target。
接下来,在去寻找左边界,和右边界了。
遍历右边界:
代码如下:
int GetRightBorder(vector<int>& nums, int target){
//给右边界赋值,如果遍历完后右边界没有变,说明target在数组的左边
int rightBorder = -2;
int left = 0;
int right = nums.size() - 1;
while(left <= right){
int middle = left + ((right - left) / 2);
if(nums[middle] > target){
right = middle - 1;
}
//我们要找右边界(包括target)这个时候如果nums[middle] <= target的话。这个时候说明
//target在我们要找的middle右边,更新left缩小范围
else if(nums[middle] <= target){
left = middle + 1;
rightBorder = middle;
}
}
return rightBorder;
}
左边界同理,整体代码如下:
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 >= 0) return {leftBorder , rightBorder };
// 情况二
return {-1, -1};
}
private:
int GetLeftBorder(vector<int>& nums, int target){
int leftBorder = -2;
int left = 0;
int right = nums.size() - 1;
while(left <= right){
int middle = left + ((right - left) / 2);
if(nums[middle] >= target){
right = middle - 1;
leftBorder = middle;
}
else if (nums[middle] < target){
left = middle + 1;
}
}
return leftBorder;
}
int GetRightBorder(vector<int>& nums, int target){
int rightBorder = -2;
int left = 0;
int right = nums.size() - 1;
while(left <= right){
int middle = left + ((right - left) / 2);
if(nums[middle] > target){
right = middle - 1;
}
else if(nums[middle] <= target){
left = middle + 1;
rightBorder = middle;
}
}
return rightBorder;
}
};
x 的平方根
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
链接:https://leetcode.cn/problems/sqrtx
class Solution {
public:
int mySqrt(int x) {
int left = 0;
int right = x;
int ans = -1;
while(left <= right){
long long mid = left +(right - left) / 2;
if(mid * mid == x) return mid;
if(mid * mid > x){
right = mid - 1;
}
if(mid * mid < x){
left = mid + 1;
ans = mid;
}
}return ans;
}
};
有效完全平方数:
和上面题目基本一样,不再多说
class Solution {
public:
bool isPerfectSquare(int num) {
int left = 0;
int right = num;
while(left <= right){
long long middle = left + (right - left) / 2;
if(middle * middle == num) return true;
if(middle * middle < num) left = middle + 1;
if(middle * middle > num) right = middle - 1;
}
return false;
}
};