【刷题(13)】二分查找

一、二分查找基础

(1)int mid = ((right - left) >> 1) + left;
(2)lower_bound的底层实现

int lower_bound(vector<int>& nums, int x) 
{
	int left = 0;
	int right = nums.size() - 1;
    // 区间为 左闭右闭
	while (left <= right) {
		int mid = left +(right - left) / 2;
		if (x > nums[mid]) {
			left = mid + 1;
		}
		else {
			right = mid - 1;	
		}
	}
	return left;
}

upper_bound用法和上面类似。只是把lower_bound的 【大于等于】 换成 【大于】 。仿函数等等全是相同的用法
底层实现

int upper_bound(vector<int>& nums, int x) {
	int left = 0;
	int right = nums.size() - 1;
 
	while (left <= right) {
		int mid = left +(right - left) / 2;
		if (x >= nums[mid]) {       //这里是大于等于
			left = mid + 1;
		}
		else {
			right = mid - 1;	
		}
	}
	return left;
}

(3)binary_search()实现

template <class ForwardIterator, class T>
bool binary_search (ForwardIterator first, ForwardIterator last, const T& val)
{
    first = std::lower_bound(first,last,val);
    return (first!=last && !(val<*first));
}

(5)二分查找

int search(vector<int>& nums, int target){
        // 二分查找区间[left, right),初始为整个区间
        int left = 0;   
        int right = nums.size();
        // 找到首个大于target的值
        while(left < right){
            int mid = left + ((right - left) >> 1);
            if(nums[mid] > target){
                right = mid;    // 找到一个大于target的值,暂存并在左半区间继续查找
            }else{
                left = mid + 1; // 没有找到大于target的值,在右半区间继续查找
            }
        }
        return right;
    }

二、35. 搜索插入位置

1 题目

在这里插入图片描述

2 解题思路

(1)由于是排序数组,所以可以使用二分法来进行目标值查找
(2)假设题意是叫你在排序数组中寻找是否存在一个目标值,那么训练有素的读者肯定立马就能想到利用二分法在 O(log⁡n)的时间内找到是否存在目标值。但这题还多了个额外的条件,即如果不存在数组中的时候需要返回按顺序插入的位置,那我们还能用二分法么?答案是可以的,我们只需要稍作修改即可。

考虑这个插入的位置 pos,它成立的条件为:

nums[pos−1]<target≤nums[pos]
其中 nums 代表排序数组。由于如果存在这个目标值,我们返回的索引也是 pos,因此我们可以将两个条件合并得出最后的目标:「在一个有序数组中找第一个大于等于 target的下标」。

问题转化到这里,直接套用二分法即可,即不断用二分法逼近查找第一个大于等于 target的下标 。下文给出的代码是笔者习惯的二分写法,ans 初值设置为数组长度可以省略边界条件的判断,因为存在一种情况是 target 大于数组中的所有数,此时需要插入到数组长度的位置。

3 code

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int n=nums.size();
        int left=0;
        int right=n-1;
        int ans=n;

        while(left<=right)
        {
            // >>1相当于/2
            int mid= left+((right-left)>>1);

            //移动逻辑
            if(target<=nums[mid])
            {
                ans=mid;
                right=mid-1;
            }
            else
            {
                left=mid+1;
            }
        }
        return ans;

    }
};

三、74. 搜索二维矩阵

1 题目

在这里插入图片描述

2 解题思路

(1)由于每行的第一个元素大于前一行的最后一个元素,且每行元素是升序的,所以每行的第一个元素大于前一行的第一个元素,因此矩阵第一列的元素是升序的。
(2)我们可以对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素,然后在该元素所在行中二分查找目标值是否存在。

3 code

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) 
    {
        // 我们可以对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素
        auto row=upper_bound(matrix.begin(),matrix.end(),target,[](const int b, const vector<int>&a)
        {
            return b<a[0];
        });

        if(row==matrix.begin())
        {
            return false;
        }

        --row;
        
        //然后在该元素所在行中二分查找目标值是否存在。
        return binary_search(row->begin(),row->end(),target);

    }
};

四、34. 在排序数组中查找元素的第一个和最后一个位置

1 题目

在这里插入图片描述

2 解题思路

在这里插入图片描述

3 code

class Solution {
private:
    /**
     * @brief 返回首个大于target的元素索引,如果不存在,返回数组长度n
     * @param nums: 输入数组
     * @param target: 目标值
     * @return: 目标值索引
    */
    int search(vector<int>& nums, int target){
        // 二分查找区间[left, right),初始为整个区间
        int left = 0;   
        int right = nums.size();
        // 找到首个大于target的值
        while(left < right){
            int mid = left + ((right - left) >> 1);
            if(nums[mid] > target){
                right = mid;    // 找到一个大于target的值,暂存并在左半区间继续查找
            }else{
                left = mid + 1; // 没有找到大于target的值,在右半区间继续查找
            }
        }
        return right;
    }
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        // 首个target如果存在,一定是首个大于target-1的元素
        int start = search(nums, target - 1);
        if(start == nums.size() || nums[start] != target){
            return {-1, -1};    // 首个target不存在,即数组中不包含target
        }
        // 找到首个大于target的元素,最后一个target一定是其前一位
        int end = search(nums, target);
        return {start, end - 1};
    }
};

五、34. 在排序数组中查找元素的第一个和最后一个位置

1 题目

在这里插入图片描述

2 解题思路

这道题要在一个旋转了的有序数组中搜索目标值,要求时间复杂度为 O(logn)。这个时间复杂度只能首先尝试二分查找,但是二分查找的前提是数组有序,这个数组并不满足,还可以用吗?

先别急,虽然这个数组不是整体有序,但它是局部有序的,我们尝试二分着去做看看会发生什么。

二分每次都需要取中点 mid,对于这个旋转的有序数组:

  • 如果当前区间 [left, right) 分别在两端有序区间之内,那么就按二分查找去做即可。
  • 如果当前区间 [left, right) 是跨越了两端有序子区间的,那么中间点 mid 总会把当前区间 [left, right] 分成两段,一段是有序的,一段是无序的:

(1)如果 nums[mid] > nums[left],肯定是左半区间有序;

(2)如果 nums[mid] < nums[right-1],肯定是右半区间有序;【之所以 -1,是因为 right 初始为数组长度 n,直接取 right 会导致越界】
在这里插入图片描述
二分的策略还是一样的,二分的关键是要判断 target 落在哪个区间。我们只能取有序的那个区间来比较,因为只有区间有序,才能 通过端点值的大小比较判断是否落入对应的区间

在这里插入图片描述
因此我们只要能够每次判断目标值落到哪个区间,就可以通过二分排除另一半的区间,并不一定要求必须整个数组有序。
在这里插入图片描述

3 code

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) >> 1);   
            if(nums[mid] == target){
                // 找到目标值,直接返回索引
                return mid;
            }
            if(nums[left] < nums[mid]){
                // 左半区间有序
                if(nums[left] <= target && target < nums[mid]){
                    right = mid;    // 目标值落入左半区间,更新右边界
                }else{  
                    left = mid + 1; // 否则在右半区间查找
                }
            }else{
                // 右半区间有序
                if(nums[mid] < target && target <= nums[right -1]){
                    left = mid + 1; // 目标值落入右半区间,更新左边界
                }else{
                    right = mid;    // 否则在左半区间查找
                }
            }
        }
        return -1;  // 如果退出循环,说明没有找到目标值,返回-1
    }
};

六、153. 寻找旋转排序数组中的最小值

1 题目

在这里插入图片描述

2 解题思路

3 code

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/660399.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[leetcode hot 150]第一百九十一题,位1的个数

题目&#xff1a; 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中设置位的个数&#xff08;也被称为汉明重量&#xff09;。 这道题比较简单&#xff0c;直接对最后一位进行与1的与操作&#xff0c;然…

【RSGIS数据资源】1981-2021年中国陆地生态系统蒸腾蒸散比数据集

文章目录 摘要基本信息数据结构和内容采集方法信息数据处理方法与数据质量 摘要 本数据集涵盖了中国陆地生态系统蒸腾蒸散比&#xff08;T/ET&#xff09;、蒸腾&#xff08;T&#xff09;及蒸散&#xff08;ET&#xff09;三组数据。基于模型-数据融合方法&#xff0c;集成PT…

android11禁止进入屏保和自动休眠

应某些客户要求&#xff0c;关闭了开机进入屏保&#xff0c;一段时间会休眠的问题。以下diff可供参考&#xff1a; diff --git a/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml b/overlay/frameworks/base/packages/SettingsProvider/res/value…

Shell编程之正则表达式与文本处理器

正则表达式的定义 正则表达式又称正规表达式&#xff0c;常规表达式。在代码中常简写为 regex、regexp 或 RE。 正则表达式是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串&#xff0c;简单来说&#xff0c; 是一种匹配字符串的方法&#xff0c;通过一些特殊符号&…

Spring +SpringMVC+Mybatis项目详细构造

一&#xff0c;文档详解 1&#xff0c;web.xml配置 配置spring监听器&#xff1a; 指定spring配置文件的位置和名称&#xff0c;扫描会先扫描此文件&#xff0c;此文件中的扫描文档作为父类扫描&#xff0c;父类扫描不可访问子类扫描&#xff0c;子类扫描可访问父类扫描 &l…

go语言的使用方法

一.go语言的介绍 1.简介 2.应用领域 3.使用go语言的公司 4.go语言开发工具介绍 5.go语言开发环境搭建 【1】搭建Go开发环境-安装和配置SDK 基本介绍: 1).SDK的全称(Software Development Kit软件开发工具包&#xff09;2).SDK是提供给开发人员使用的&#xff0c;其中包含了…

记一次SpringCloud OpenFeign 服务调用传递 token @Async 上下文信息获取失败

一、场景 在异步方法中使用了feign调用&#xff0c;发现提示“您还未登录或登录已失效”。那原因很明了就是我的登录信息没办法传入到feign的调用方法里。 二、考虑的解决办法 1&#xff09;尝试一&#xff1a;ServletRequestAttributes attributes (ServletRequestAttrib…

高维数组到向量的转换:两种方法的深度解析

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;高维数组的挑战与需求 二、方法一&#xff1a;使用NumPy库进行展平 示…

【数据分析面试】56.数据格式转换(Python:melt函数)

题目 给定一个df&#xff0c;包含ABCDE多个列。请编写一个 Python 程序&#xff0c;将列 ‘D’ 和 ‘E’ 转换为长格式&#xff0c;并使用 ‘A’、‘B’ 和 ‘C’ 作为标识符。 换句话说&#xff0c;将数据中的D、E两列转换为行&#xff0c;使数据从宽变长。 示例&#xff1…

BH-0.66 6000/5/150电流互感器 塑壳 JOSEF约瑟

BH-0.66 15/5塑壳式电流互感器 BH-0.66 20/5塑壳式电流互感器 BH-0.66 30/5塑壳式电流互感器 BH-0.66 40/5塑壳式电流互感器 BH-0.66 50/5塑壳式电流互感器 BH-0.66 75/5塑壳式电流互感器 BH-0.66 100/5塑壳式电流互感器 BH-0.66 150/5塑壳式电流互感器 BH-0.66 200/5塑壳式…

职校老师的工资待遇怎么样

工资水平一直是教师们关注的焦点&#xff0c;毕竟&#xff0c;工资不仅关系到个人的生活品质&#xff0c;还影响着教师的职业满意度和工作动力。职校教师的工资待遇究竟是怎样的呢&#xff1f; 职校教师的工资水平受多种因素影响&#xff0c;包括地区、学校类型、个人资历和教学…

【OrangePi AIpro】香橙派 AIpro 为AI而生

产品简介 OrangePi AIpro(8T)&#xff1a;定义边缘智能新纪元的全能开发板 在当今人工智能与物联网技术融合发展的浪潮中&#xff0c;OrangePi AIpro(8T)凭借其强大的硬件配置与全面的接口设计&#xff0c;正逐步成为开发者手中的创新利器。这款开发板不仅代表了香橙派与华为…

pyqt Qtreeview分层控件

pyqt Qtreeview分层控件 介绍效果代码 介绍 QTreeView 是 PyQt中的一个控件&#xff0c;它用于展示分层数据&#xff0c;如目录结构、文件系统等。QTreeView 通常与模型&#xff08;如 QStandardItemModel、QFileSystemModel 或自定义模型&#xff09;一起使用&#xff0c;以管…

【机器学习300问】105、计算机视觉(CV)领域有哪些子任务?

计算机视觉作为人工智能的重要分支&#xff0c;发展至今已经在诸多领域取得显著的成果。在众多的计算机视觉任务中&#xff0c;图像分类、目标检测与定位、语义分割和实例分割是四个基本而关键的子任务&#xff0c;它们在不同的应用场景下扮演着重要角色。这四个子任务虽然各具…

【408真题】2009-23

“接”是针对题目进行必要的分析&#xff0c;比较简略&#xff1b; “化”是对题目中所涉及到的知识点进行详细解释&#xff1b; “发”是对此题型的解题套路总结&#xff0c;并结合历年真题或者典型例题进行运用。 涉及到的知识全部来源于王道各科教材&#xff08;2025版&…

Ps系统教程03

选区工具的组合使用 先用魔棒将大致区域点击圈主 会发现一些零散的小区域 使用套索工具进行区域的加减&#xff08;按住shift/alt键进行相关区域加减&#xff09; 可以放大查看 基本处理完细节之后 如果把不用的填充背景直接按delete删除&#xff0c;那么原版图案就会…

研学活动报名二维码怎么制作?

在组织研学活动时&#xff0c;老师们经常面临报名流程繁琐、信息收集不全面、统计工作耗时等问题&#xff1f;如何高效地管理学生的报名信息&#xff0c;确保活动顺利进行呢&#xff1f; 现在我们有了更多的选择。老师们可以快速制作出研学活动的研学活动报名二维码怎么制作&am…

深度解析搜索引擎广告(SEM)与社交媒体广告(SMM):NetFarmer助力企业数字化出海

在当今数字化时代&#xff0c;企业出海已经成为了一个必然趋势。然而&#xff0c;如何有效地在海外市场中推广品牌、吸引潜在客户&#xff0c;成为了众多企业面临的重要挑战。搜索引擎广告&#xff08;SEM&#xff09;和社交媒体广告&#xff08;SMM&#xff09;作为两种主要的…

Ex 防爆标准解读

以如下标准为例&#xff1a; Ex t IIIB T2 40 Db 解读&#xff1a; Ex防爆 t&#xff1a; IIIB T2 40 T2为温度等级&#xff0c;40为最大表面温度40度 Db 设备防护用于22区 类似铭牌为

能芯(EnChip)模拟芯片应用和选型

数据显示&#xff0c;超过60%的驾驶者会在开车时听音乐&#xff0c;这不仅可以提高驾驶者的注意力&#xff0c;还可以缓解驾驶过程中产生的疲劳和压力&#xff0c;特别是在长途驾驶或交通拥堵时尤其明显。基于音乐欣赏&#xff0c;高保真音质是音响系统的核心指标之一&#xff…