海西蒙古族藏族自治州网站建设_网站建设公司_企业官网_seo优化
2026/1/5 22:09:30 网站建设 项目流程

胖咕噜的稞达鸭:个人主页

个人专栏: 《数据结构》《C++初阶高阶》
《Linux系统学习》
《算法入门》

⛺️技术的杠杆,撬动整个世界!

在这里插入图片描述

二分查找算法

https://leetcode.cn/problems/binary-search/description/

在这里插入图片描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果 target 存在返回下标,否则返回 -1
你必须编写一个具有 O(log n) 时间复杂度的算法。
解法一:暴力解法->O(N)
从数组下标索引为0访问到数组末尾下标;
解法二:二分查找算法,当数组有二段性的时候就可以二分查找,先排序,拿取到

class Solution {
public:
int search(vector<int>& nums, int target) {int left=0,right=nums.size()-1;while(left<=right){//int mid=(left+right)//不建议这样写,会有溢出风险int mid =(right-left)/2+left;if(nums[mid]>target)right=mid-1;else if(nums[mid]<target)left=mid+1;else return mid;}return -1;}};

总结朴素二分模板:

while(left <= right)//(用于循环结束条件)
{
int mid =left+(right-left)/2;//(求中间点,防止溢出风险)
if(...)
{
left = mid+1;
}
else if(...)
{
right = mid-1;
}
else
return ...
}

排序数组中查找元素的第一个和最后一个位置

[[https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/]]
在这里插入图片描述

题目解析

非递减顺序排列的整数数组nums,和一个目标值target,给定目标值在数组中的开始位置和结束位置。
举例:nums = [ 5,7, 7, 8 ,8, 10],target = 8;返回位置【3, 4】

算法原理

解法一:暴力枚举,遍历一遍数组,找到target值,在这个位置标记为begin,向后遍历,找到最后一个符合target ,标记为end,然后返回[begin,end]这段区间的起始位置,这样果不其然就会超时。
解法二:朴素二分查找
定义left在数组索引为0的位置,right在数组最后一个位置。

查找区间左端点:

先查找中间点的位置,mid,如果

  1. mid < target ,left = mid + 1;
  2. mid >= target,right = mid (不同于朴素二分查找,right不能小于mid)

那么怎么判断循环条件(left < right):
问题:为什么不能加等号?
这里分三种不用进循环的情况:

  1. 有结果:区间内left=right的时候,就是最终结果,无需判断;
  2. 区间内部的值都是大于target,right一定会向左移动,一直移动到left的位置,判断以下,left是否等于target;
  3. 区间内部的值都是小于target,left向右移动,一直移动到right的位置,判断以下,right是否等于target;

如果判断了就会进入死循环。
求中点的操作:

  1. left+(right-left)/2;
  2. left+(right-left+1)/2;
    1得到的中点靠左边的位置,2得到的中点靠右边的位置,如果判断到最后,也就是left在right相邻位置,
    1会得到mid刚好在left的位置,此时left移动,left=mid+1,也就是落在了right的位置,right=left,此时结束循环的条件;
    2会得到mid在right的位置,此时right=mid,并且left<right没有结束循环,
    left+(right-left+1)/2,mid再次落到right 的位置,left<right,还是满足循环的条件,mid不停地落在right的位置,一直满足循环的条件,但是mid位置不发生任何变化,结果就是陷入死循环。
查找区间右端点:
  1. 如果mid<=target,移动left,left=mid,接着在【left,right】的位置中查找;
  2. 如果mid >target,移动right,right=mid-1,接着在【left,right】的区间中查找。

处理细节:
循环条件:必须是left<right;一旦相等就会死循环。
求中点的方式:

  1. left+(right-left)/2;
  2. left+(right-left+1)/2;
    只能用left+(right-left+1)/2,理由:
    如果判断到最后,left跟right相邻的位置,
    1求到的midleft的位置,left=mid,此时left<right,满足循环的条件,不会结束循环,再次求mid,left=mid,依旧满足循环的条件,mid的位置不发生任何变化,结果就是会陷入死循环;
    2求到的mid是right的位置,此时right=mid-1,刚好落在了left的位置,right=left,结束循环。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {//处理边界情况if(nums.size() == 0)return {-1,-1};int begin = 0;int end=0;//查找左端点int left = 0,right = nums.size()-1;while(left<right){int mid = left+(right-left)/2;if(nums[mid]<target)left=mid+1;else right=mid;}//判断是否有结果if(nums[left]!= target)return{-1,-1};else 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[right]!=target)return {-1,-1};else end = right;//最后返回最终结果return {begin,end};}};
总结二分模板:
1. 查找左端点的模板:
while(left<right)
{
int mid = left+(right-left)/2;
if(......)left=mid+1;
else right=mid;
}
2. 查找右端点的模板:
while(left<right)
{
int mid = left+(right-left+1)/2;
if(......)left=mid;
else right=mid-1;
}

如果硬要选择死记硬背(阴险阴险嘿嘿嘿)可以找规律,当下面出现-1的时候,上面就+1。(右端点)

第一个错误的版本

https://leetcode.cn/problems/first-bad-version/description/
在这里插入图片描述

题目解析:
本质上就是查找左端点的问题,左端点到整个数组末尾都是不符合要求的错误版本,由于我们要查找的是
第一个错误版本,所以返回的是左端点的下标。

这里有一个小的细节,在定义左端点left,和右断点right,left不可以从索引为0的位置开始,根据现实情况,如果第一个版本的产品不合格,就一定不会在不合格产品的基础上开发更高版本的产品。由于要计算中间点,所以相应的right=n.

而且如果mid是错误版本,就说明第一个错误版本一定在[1,mid-1]中,right=mid-1;
如果mid不是错误版本,就说明第一个错误版本在[mid+1,n]中,left= mid+1;

class Solution {
public:
int firstBadVersion(int n) {
int left=1,right=n;
while(left<=right)
{
int mid =left+(right-left)/2;
//如果中间值mid是错误版本,说明【1,mid】中有第一个错误版本
if(isBadVersion(mid))right = mid - 1;
else left = mid+1;//如果中间值mid不是错误版本,说明[mid+1,n]中会有第一个错误版本
}
return left;
}
};

查找x的平方根

https://leetcode.cn/problems/sqrtx/description/

在这里插入图片描述

class Solution {
public:
int mySqrt(int x) {
if(x == 0)return 0;//题目要求是非负整数(正整数和0),如果是0,就直接返回0
int left=1,right=x;
while(left<right)//如果left=right=1,就不用判断了
{
long long mid = left + (right-left+1)/2;//防止溢出,用long long
if(mid * mid <= x)left=mid;
else right = mid-1;
}
return left;
}
};

搜索插入位置

https://leetcode.cn/problems/search-insert-position/
在这里插入图片描述

解法一:暴力解法
解法二:二分查找

class Solution {
public:
int searchInsert(vector<int>& nums, int target) {int left=0,right=nums.size()-1;if(target>nums[right])return right+1;//如果要插入的值比整个数组中的值都大,按照元素顺序返回right+1while(left<right){int mid = left+(right-left)/2;if(nums[mid]<target)left=mid+1;else right = mid;}return left;}};

山脉数组的封顶索引

https://leetcode.cn/problems/peak-index-in-a-mountain-array/description/
在这里插入图片描述

class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {int left=1,right=arr.size();while(left<right){int mid=left+(right-left+1)/2;if(arr[mid]>arr[mid-1])left=mid;else right=mid-1;}return left;}};

在这里插入图片描述

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询