二分查找习题篇(上)

news2024/11/9 1:25:58

二分查找习题篇(上)

1.二分查找

题目描述:

给定⼀个 n 个元素有序的(升序整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

​输入: nums = [-1,0,3,5,9,12], target = 9

​输出: 4

解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2

输出: -1

解释: 2 不存在 nums 中因此返回 -1

提示:

你可以假设 nums 中的所有元素是不重复的。

n 将在 [1, 10000]之间。

nums 的每个元素都将在 [-9999, 9999]之间。

解法一:暴力解法

从前往后枚举每一个元素,将其与目标值进行对比。

时间复杂度最差为O(N)。

解法二:二分查找算法

当数组具有“二段性”时,我们就可以用二分查找算法。

算法思路:

  1. 定义 left , right 指针,分别指向数组的左右区间。

  2. 当left<=right时,下列一直循环:

​ 找到待查找区间的中间点 mid ,找到之后分三种情况讨论:

  • arr[mid] == target:返回 mid 的值;
  • arr[mid] > target:让 right = mid - 1,在 [left, right] 的区间继续查找 ,重复 2 过程;
  • arr[mid] < target:让 left = mid + 1, 在 [left, right] 的区间继续查找,重复 2 过程;
  1. 当left>right时,说明整个区间都没有这个数,返回 -1 。

细节问题:

1.循环结束的条件

当left>right

2.为什么是正确的?

二分查找是从暴力解法优化而来的

3.时间复杂度

1次——>n/21=n/2

2次——>n/22=n/4

3次——>n/23=n/8

…次——>…

x次——>n/2x=1(当left==right,找到要找的元素时)

2x=n——>x=logN

因此,二分查找的时间复杂度是logN.

代码实现:

class Solution {
public:
    int search(vector<int>& nums, int target) 
    {
        int left=0,right=nums.size()-1;
        while(left<=right)//每次查找的元素都是未知的,所以要取等号
        {
            int mid=left+(right-left)/2;  //防止溢出
            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; //防止溢出,替换成left+(right-left+1)也可以,只不过是偶数个元素时,mid有2个中间值,左右均可
​ if(……)

​ right=mid-1;
​ else if(……)

​ left=mid+1;
​ else

​ return ……;
​ }

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

题目描述:

给你一个按照非递减顺序排列(趋势要么递增要么不变)的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

​ 输入:nums = [5,7,7,8,8,10], target = 8
​ 输出:[3,4]

示例 2:

​ 输入:nums = [5,7,7,8,8,10], target = 6
​ 输出:[-1,-1]

示例 3:

​ 输入:nums = [], target = 0
​ 输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

解法一:暴力查找

从前往后枚举每一个元素,将其与目标值进行对比,相同时记为begin,继续向后寻找,直到找到该数的末尾位置记为end。返回begin,end即可。

时间复杂度最差为O(N)。

解法二:二分查找

当数组具有“二段性”时,我们就可以用二分查找算法。接下来我们来寻找该数组的“二段性”.

记左边界为Bleft,右边界为Bright.

1.查找区间的左端点

这里,我们把数组的元素分为两部分——小于target的部分[left,Bleft-1] and 大于等于target的部分[Bleft,right]。

  • 当mid在[left,Bleft-1]的区间,要找左区间,我们可以直接舍去[left,mid],更新left=mid+1;
  • 当mid在[Bleft,right]的区间,要找左区间,我们可以直接舍去[mid+1,right],更新right=mid(因为mid可能是最终结果);
  • 之后在[left,right]上继续寻找左边界。

细节处理:

1.循环条件:

left<right;

  • left=right时,就是最终结果,无需判断;如果判断,就会死循环。

2.求中点的操作:

  • 正确写法:left+(right-left)/2:求的是靠左的位置(向下调整)。

  • 找左端点时求中点要向下取整

    要是向上调整,判断之后,当mid在[Bleft,right]时,要更新right=mid。再进行下一次判断,要是又面对同样的情况,又更新right=mid……又循环往复……

2.查找区间右端点:

这里,我们把数组的元素分为两部分——小于等于target的部分[left,Bright] and 大于target的部分[Bright+1,right]。

  • 当mid在[left,Bright]的区间,要找右区间,我们可以直接舍去[left,mid-1],更新left=mid(因为mid可能是最终结果);
  • 当mid在[Bright+1,right]的区间,要找右区间,我们可以直接舍去[mid,right],更新right=mid-1;
  • 之后在[left,right]上继续寻找右边界。

细节处理:

1.循环条件:

left<right

2.求中点的操作:

  • 正确写法:left+(right-left+1)/2:求的是靠右的位置(向上调整);

  • 找右端点时求中点要向上取整

    要是向下调整,判断之后,当mid落在[left,Bright]时,要更新left=mid。再进行下一次判断,要是又面对同样的情况,又要更新left=mid……又循环往复……

代码实现:
class Solution
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        // 处理边界情况
        if(nums.size() == 0) 
            return {-1, -1};

        int begin = 0;
        // 1. 二分左端点
        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; // 标记⼀下左端点
        
        // 2. 二分右端点
        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;
        }
        
        return {begin, right};
    }
};

查找区间左端点的模版:

while(left < right)
{
int mid = left + (right - left) / 2;
if(……) left = mid + 1;
else right = mid;
}

查找区间右端点的模版:

while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(……) left = mid;
else right = mid - 1;
}

总结切记:分类讨论的代码,就题论题即可。

3.搜索插入位置

题目描述:

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

​ 输入: nums = [1,3,5,6], target = 5
​ 输出: 2

示例 2:

​ 输入: nums = [1,3,5,6], target = 2
​ 输出: 1

示例 3:

​ 输入: nums = [1,3,5,6], target = 7
​ 输出: 4

算法思路:

本题数组是一个排序数组,具有“二段性”时,我们就可以用二分查找算法。

  1. 设插入位置的坐标为x,根据插入位置的特点可以把数组的元素分为两部分——小于target的部分[left , x-1] and 大于等于target的部分[x , right]。
  • 当mid在[left, x-1]的区间,我们可以直接舍去[left, mid],更新left=mid+1;
  • 当mid在[x, right]的区间,我们可以直接舍去[mid+1,right],更新right=mid(因为mid可能是最终结果);
  • 之后在[left,right]上继续查找。
  1. 直到我们的查找区间的长度变为 1 ,也就是 left == right 的时候, left 或者right 所在的位置就是我们要找的结果。

代码实现:

class Solution
{
public:
    int searchInsert(vector<int>& nums, int target) 
    {
        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 right + 1;
        return right;
    }
};

4.x 的平方根

题目描述:

给你一个非负整数 x ,计算并返回 x算术平方根

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

**注意:**不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5

示例 1:

​ 输入:x = 4
​ 输出:2

示例 2:

​ 输入:x = 8
​ 输出:2

解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。

解法一:暴力查找

算法思路:

依次枚举 [0, x] 之间的所有数 i :

  • 如果 i * i == x ,直接返回 x ;

  • 如果 i * i > x ,说明之前的⼀个数是结果,返回 i - 1 。

由于 i * i 可能超过 int 的最大值,因此使用 long long 类型。

代码实现:
class Solution {
public:
    int mySqrt(int x) {
        // 由于两个较⼤的数相乘可能会超过 int 最⼤范围
        // 因此⽤ long long
        long long i = 0;
        for (i = 0; i <= x; i++)
        {
            // 如果两个数相乘正好等于 x,直接返回 i
            if (i * i == x) return i;
            // 如果第⼀次出现两个数相乘⼤于 x,说明结果是前⼀个数
            if (i * i > x) return i - 1;
        }
        // 为了处理oj题需要控制所有路径都有返回值
        return -1;
    }
};

解法二:二分查找

本题数组具有“二段性”时,我们就可以用二分查找算法。

算法思路:

这里,我们把数组的元素分为两部分——平方后小于等于x的部分[1,mid] and 平方后大于x的部分[mid-1, x]。

  1. 定义 left , right 指针,分别指向数组的左右区间。

  2. 当left<right时,下列一直循环:

​ 找到待查找区间的中间点 mid ,找到之后分三种情况讨论:

  • mid*mid<=x:更新left=mid
  • mid*mid>x:更新right=mid-1
代码实现:
class Solution
{
public:
    int mySqrt(int x) 
    {
        if(x < 1) return 0; // 处理边界情况
        int left = 1, right = x;
        while(left < right)
        {
            long long mid = left + (right - left + 1) / 2; // 防溢出
            if(mid * mid <= x) left = mid;
            else right = mid - 1;
        }
        return left;
    }
};

最后,本篇文章到此结束,感觉不错的友友们可以一键三连支持一下笔者,有任何问题欢迎在评论区留言哦~

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

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

相关文章

RabbitMQ客户端应用开发实战

这一章节我们将快速完成RabbitMQ客户端基础功能的开发实战。 一、回顾RabbitMQ基础概念 这个RabbitMQ的核心组件&#xff0c;是进行应用开发的基础。 二、RabbitMQ基础编程模型 RabbitMQ提供了很多种主流编程语言的客户端支持。这里我们只分析Java语言的客户端。 上一章节提…

RNA-seq 差异分析的点点滴滴(1)

引言 本系列[1])将开展全新的转录组分析专栏&#xff0c;主要针对使用DESeq2时可能出现的问题和方法进行展开。 为何使用未经标准化的计数数据&#xff1f; DESeq2 工具包在接收输入时&#xff0c;期望得到的是未经处理的原始计数数据&#xff0c;比如从 RNA-seq 或其他高通量测…

RTC精度及校准

RTC精度偏差&#xff1a; RTC的基准时间和精度与石英晶体的频率相关&#xff0c;晶体的谐振频率取决于温度&#xff0c;因此RTC性能与温度相关&#xff0c;晶体的频率偏差是晶体正常频率的温度反转函数。 一、硬件方面&#xff1a; 1.使用高精度振荡器的RTC模块&#xff1b; …

智慧城市路面垃圾识别系统产品介绍方案

方案介绍 智慧城市中的路面垃圾识别算法通常基于深度学习框架&#xff0c;这些算法因其在速度和精度上的优势而被广泛采用。这些模型能够通过训练识别多种类型的垃圾&#xff0c;包括塑料袋、纸屑、玻璃瓶等。系统通过训练深度学习模型&#xff0c;使其能够识别并定位多种类型…

【安当产品应用案例100集】029-使用安全芯片保护设备核心业务逻辑

我国工业企业普遍缺乏数据安全意识&#xff0c;对数据安全保护缺乏基本认识。这导致企业在数据安全方面的投入不足&#xff0c;保护能力基本不具备&#xff0c;难以有效应对数据安全风险。不过随着安全事件越来越多&#xff0c;很多工业企业的安全意识也越来越高&#xff0c;在…

遥控器工作核心技术以及传输信号算法详解!

一、遥控器传输信号算法 无线通信技术&#xff1a;无人机遥控器信号传输算法主要基于无线通信技术&#xff0c;通过特定的调制、编码和信号处理技术&#xff0c;将遥控器的操作指令转化为无线电信号&#xff0c;并传输给被控制设备。被控制设备接收到信号后&#xff0c;再将其…

性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台

前言 在当前激烈的市场竞争中&#xff0c;创新和效率成为企业发展的核心要素之一。在这种背景下&#xff0c;如何保证产品和服务的稳定性、可靠性以及高效性就显得尤为重要。 而在软件开发过程中&#xff0c;性能测试是一项不可或缺的环节&#xff0c;它可以有效的评估一个系…

电力调度控制台安全性有没有保障

电力调度控制台作为电力系统的核心组成部分&#xff0c;承担着监控、控制和调度电网运行的重要任务。随着电力系统的不断发展和智能化水平的提升&#xff0c;电力调度控制台的安全性问题也日益受到关注。那么&#xff0c;电力调度控制台的安全性究竟有没有保障呢? 从技术层面来…

练习LabVIEW第四十四题

学习目标&#xff1a; 计算学生三门课(语文&#xff0c;数学&#xff0c;英语)的平均分&#xff0c;并根据平均分划分成绩等级。要求输出等级A,B,C,D,E。90分以上为A&#xff0c;80&#xff5e;89为B&#xff0c;70&#xff5e;79为C&#xff0c;60&#xff5e;69为D&#xff…

录音怎么转换成文字?这5款视频语音转文字工具轻松搞定!

在各类会议、讲座、采访及日常学习活动中&#xff0c;录音已成为我们记录关键信息的常用手段。然而&#xff0c;面对那些可能长达数小时甚至更久的录音文件&#xff0c;如何快速有效地将它们转换成可编辑、可搜索的文字资料&#xff0c;确实是一个令人困扰的问题。为此&#xf…

蓝桥杯c++算法学习【1】之枚举与模拟(卡片、回文日期、赢球票:::非常典型的比刷例题!!!)

别忘了请点个赞收藏关注支持一下博主喵&#xff01;&#xff01;&#xff01; 关注博主&#xff0c;更多蓝桥杯nice题目静待更新:) 枚举与模拟 一、卡片&#xff1a; 【问题描述】 …

面试题:Spring(一)

1. Spring框架中bean是单例么&#xff1f; Service Scope("singleton") public class UserServiceImpl implements UserService { }singleton : bean在每个Spring IOC容器中只有一个实例。prototype&#xff1a;一个bean的定义可以有多个实例。 2. Spring框架中的…

InnoDB 存储引擎<七>通用表空间+临时表空间

目录 通⽤表空间 - General Tablespace 临时表空间 - Temporary Tablespaces 通⽤表空间 - General Tablespace 对应磁盘上的文件需要用户手动创建 1.通⽤表空间的作⽤和特性&#xff1f; 解答问题&#xff1a; 1.作用&#xff1a;可以把数据量比较小且强相关的表&#xff…

linux 通过apt安装软件包时出现依赖包版本不对的问题解决

通过网上查找解决办法时&#xff0c;发现的解决办法无法完美解决问题: 比如通过安装对应版本解决 如: sudo apt-get install xxx2.7.0ubuntu 这样会先卸载原先包&#xff0c;在安装对应版本的包 或者直接删除依赖的包 sudo apt-get purge xxxx 如果碰到底层包的话&#xf…

用示例来看C2Rust工具的使用和功能介绍

C2Rust可以将C语言的源代码转换成Rust语言的源代码。下面是一个简单的C语言代码示例,以及使用c2Rust工具将其转换为Rust安全代码的过程。 C语言源代码示例 // example.c #include <stdio.h>int add(int a, int b)

提升电机控制的关键:东芝TB6605FTG深度解析

在电机控制领域&#xff0c;对高效、精确且可靠的组件的需求是不可忽视的。东芝的TB6605FTG正是一款具备这些特性的电机驱动IC&#xff0c;是工程师们在设计电机控制系统时的理想选择。 主要特性与规格 TB6605FTG的核心是一个三相全波电机驱动IC&#xff0c;专为无刷直流电机控…

动态规划理论基础和习题【力扣】【算法学习day.25】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…

【K8S系列】K8S 集群 CPU 爆满导致 Pod Pending 状态的分析与解决方案

在 Kubernetes 集群中&#xff0c;CPU 突然爆满可能导致 Pod 状态变为 Pending&#xff0c;影响应用的可用性。本文将深入分析其原因&#xff0c;并附上相关命令及其执行结果&#xff0c;帮助你更好地理解和解决此问题。 1. 问题描述 在 Kubernetes 集群中&#xff0c;当 CPU …

开源代码管理平台Gitlab如何本地化部署并实现公网环境远程访问私有仓库

文章目录 前言1. 下载Gitlab2. 安装Gitlab3. 启动Gitlab4. 安装cpolar5. 创建隧道配置访问地址6. 固定GitLab访问地址6.1 保留二级子域名6.2 配置二级子域名 7. 测试访问二级子域名 前言 本文主要介绍如何在Linux CentOS8 中搭建GitLab私有仓库并且结合内网穿透工具实现在公网…

Linux磁盘存储

磁盘存储 设备文件 设备文件是类Unix操作系统&#xff08;包括Linux&#xff09;中一种特殊的文件类型&#xff0c;它代表了设备接口&#xff0c;使得用户空间的程序可以通过标准的文件操作来访问和控制硬件设备。设备文件为周边设备提供了简单的接口&#xff0c;如打印机、硬…