【刷题(13)】二分查找

news2024/9/21 12:46: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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA 17

文章目录 概述一 语法层面变化1_JEP 409&#xff1a;密封类2_JEP 406&#xff1a;switch模式匹配&#xff08;预览&#xff09; 二 API层面变化1_JEP 414&#xff1a;Vector API&#xff08;第二个孵化器&#xff09;2_JEP 415&#xff1a;特定于上下文的反序列化过滤器 三 其他…

【Linux进程篇】Linux进程管理——进程创建与终止

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 目录 进程创建 fork函数初识 写时拷贝 fork常规用法 fork调用失败的原因 进程终止 进程退出场景 _exit函数 exit函数 return退出 进程创建 fork函数初识 在linux中fork函数时非常重要的函数&#xff0c;它从已…

Javascript 基础知识 —— 重写数组方法

1、写一个函数&#xff0c;实现深度克隆对象 const obj {name: "LIYUFAN",age: 25,career: "初级前端工程师",info: {field: ["JS", "CSS", "HTML"],framework: ["React", "Vue", "Angular"…

jmeter多用户并发登录教程

有时候为了模拟更真实的场景&#xff0c;在项目中需要多用户登录操作&#xff0c;大致参考如下 jmx脚本&#xff1a;百度网盘链接 提取码&#xff1a;0000 一&#xff1a; 单用户登录 先使用1个用户登录&#xff08;先把1个请求调试通过&#xff09; 发送一个登录请求&…

DSM驾驶行为分析系统在渣土车管理中的应用

随着科技的不断进步&#xff0c;智能交通系统正逐渐成为现代交通管理的重要工具。其中&#xff0c;DSM驾驶行为分析系统以其独特的功能和优势&#xff0c;在提升驾驶安全性、优化驾驶员管理等方面发挥着重要作用。索迪迈科技将DSM驾驶行为分析系统成功应用于渣土车管理中&#…

深兰科技获评2024年度人工智能出海先锋奖

5月25日&#xff0c;以“亚洲新势力&#xff1a;创新、融合与可持续发展”为主题的亚洲品牌经济峰会2024深圳会议在深圳益田威斯汀酒店举办。本次活动由中国亚洲经济发展协会指导&#xff0c;亚洲国际品牌研究院主办&#xff0c;旨在搭建品牌创新与经济发展交流平台&#xff0c…

服务器感染了. rmallox勒索病毒,如何确保数据文件完整恢复?

导言&#xff1a; 近年来&#xff0c;随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显。其中&#xff0c;勒索病毒作为一种严重的网络威胁&#xff0c;对个人和企业数据造成了巨大的威胁。本文将重点介绍.rmallox勒索病毒的特点、传播途径以及应对策略&#xff0c;旨…

基于 FastAI 文本迁移学习的情感分类(93%+Accuracy)

前言 系列专栏:【深度学习&#xff1a;算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域&#xff0c;讨论了各种复杂的深度神经网络思想&#xff0c;如卷积神经网络、循环神经网络、生成对…

J.搬砖【蓝桥杯】/01背包+贪心

搬砖 01背包贪心 思路&#xff1a;要让重量更小的在更前面&#xff0c;价值更大的在更后面&#xff0c;vi−wj>vj−wi viwi>vjwj 第 i 个箱子放在第 j 个箱子下面就显然更优。所以进行排序再用01背包即可。 #include<iostream> #include<algorithm> #defi…

vs工程添加自定义宏

一、简介 用户可以添加自定义宏变量方便工程路径名称的修改和配置 例&#xff1a;$(SolutionDir) 为解决方案路径&#xff0c;$(PojectDir) 为工程所在路径 测试环境&#xff1a;vs2017&#xff0c;qt5.14.0 二、配置 1、打开属性窗口&#xff1a;视图-》其他窗口-》属性管…

Android笔记--应用安装

这一节了解一下普通应用安装app的方式&#xff0c;主要是唤起系统来安装&#xff0c;直接上代码: 申请权限 <uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name"android.permission.WRITE_EXT…

HaloDB 的 Oracle 兼容模式

↑ 关注“少安事务所”公众号&#xff0c;欢迎⭐收藏&#xff0c;不错过精彩内容~ 前倾回顾 前面介绍了“光环”数据库的基本情况和安装办法。 哈喽&#xff0c;国产数据库&#xff01;Halo DB! 三步走&#xff0c;Halo DB 安装指引 ★ HaloDB是基于原生PG打造的新一代高性能安…

Python Selenium 详解:实现高效的UI自动化测试

落日余辉&#xff0c;深情不及久伴。大家好&#xff0c;在当今软件开发的世界中&#xff0c;自动化测试已经成为保障软件质量和快速迭代的重要环节。而在自动化测试的领域中&#xff0c;UI自动化测试是不可或缺的一部分&#xff0c;它可以帮助测试团队快速验证用户界面的正确性…

爬虫案例(读书网)

一.我们还是使用简单的bs4库和lxml&#xff0c;使用xpath&#xff1a; 导入下面的库&#xff1a; import requests from bs4 import BeautifulSoup from lxml import etree 我们可以看见它的div和每个书的div框架&#xff0c;这样会观察会快速提高我们的简单爬取能力。 二.实…

Nginx网页服务

nginx的配置: 1、全局块&#xff1a;全局配置&#xff0c;对全局生效&#xff1b; 2、events块&#xff1a;配置影响 Nginx 服务器与用户的网络连接&#xff1b; 3、http块&#xff1a;配置代理&#xff0c;缓存&#xff0c;日志定义等绝大多数功能和第三方模块的配置&#xf…

C语言---文件操作

【C语言详解】——文件操作&#xff08;建议收藏&#xff09;_c语言 写文件原理-CSDN博客 一、文件的读取 # define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<errno.h> #include<string.h>int main() {FILE * pffopen("C:\\Users\\zhw\\De…

Java中的ORM框架——myBatis

一、什么是ORM ORM 的全称是 Object Relational Mapping。Object代表应用程序中的对象&#xff0c;Relational表示的是关系型数据库&#xff0c;Mapping即是映射。结合起来就是在程序中的对象和关系型数据库之间建立映射关系&#xff0c;这样就可以用面向对象的方式&#xff0c…

Vue3使用Composition API实现响应式

title: Vue3使用Composition API实现响应式 date: 2024/5/29 下午8:10:24 updated: 2024/5/29 下午8:10:24 categories: 前端开发 tags: Vue3CompositionRefsReactiveWatchLifecycleDebugging 1. 介绍 Composition API是Vue.js 3中新增的一组API&#xff0c;用于在组件中组…

服装服饰商城小程序的作用是什么

要说服装商家&#xff0c;那数量是非常多&#xff0c;厂家/经销门店/小摊/无货源等&#xff0c;线上线下同行竞争激烈&#xff0c;虽然用户群体广涵盖每个人&#xff0c;但每个商家肯定都希望更多客户被自己转化&#xff0c;渠道运营方案营销环境等不可少。 以年轻人为主的消费…

30【Aseprite 作图】桌子——拆解

1 桌子只要画左上方&#xff0c;竖着5&#xff0c;斜着3个1&#xff0c;斜着两个2&#xff0c;斜着2个3&#xff0c;斜着一个5&#xff0c;斜着一个很长的 然后左右翻转 再上下翻转 在桌子腿部分&#xff0c;竖着三个直线&#xff0c;左右都是斜线&#xff1b;这是横着水平线不…