算法每日双题精讲 —— 二分查找(二分查找,在排序数组中查找元素的第一个和最后一个位置)

🌟快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 🌟 

别再犹豫了!快来订阅我们的算法每日双题精讲专栏,一起踏上算法学习的精彩之旅吧!💪 


      在算法的浩瀚星空中✨,二分查找算法宛如一颗璀璨的明星🌟,闪耀着简洁与高效的光芒。今天,就让我们一同聚焦于 “二分查找” 以及 “在排序数组中查找元素的第一个和最后一个位置” 这两道经典题目,深度挖掘二分查找算法的奥秘与应用技巧🤓。

不要跳过每一个二分查找算法文章哦,难度逐渐提升 !


目录

 二分查找简介

一、二分查找

📖题目描述

🧠讲解算法原理

💻代码实现(以 C++ 为例)

 复杂度分析

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

📖题目描述

🧠讲解算法原理

💻代码实现(以 C++ 为例)

复杂度分析


二分查找简介

特点:最恶心,细节最多,最容易写出死循环,但是掌握就是最简单的算法

二分查找有三个模板:

  1. 朴素的二分模板——easy,局限
  2. 查找左边界的二分模板——万能,细节多
  3. 查找右边界的二分模板


一、二分查找

题目链接👉【力扣】

📖题目描述

 

🧠讲解算法原理

        二分查找的核心思想犹如在一本有序的字典中查找单词📕,每次都能通过中间元素将搜索区间大致缩小一半。"二段性"
        首先,我们设定两个指针,left 指向数组的起始位置,right 指向数组的末尾位置。然后,在循环中计算中间索引 mid = left + (right - left) / 2(这种计算方式可有效避免整数溢出)。接下来,比较 nums[mid] 与 target 的大小关系:

  • 若 nums[mid] == target,则幸运地找到了目标值,直接返回 mid 😀。
  • 若 nums[mid] < target,这表明目标值在中间元素的右侧,于是将 left 更新为 mid + 1,继续在右侧区间查找。
  • 若 nums[mid] > target,意味着目标值在中间元素的左侧,此时将 right 更新为 mid - 1,继续在左侧区间探索。
    循环持续进行,直到 left 大于 right,表示整个数组都已搜索完毕但未找到目标值,此时返回 -1。

💻代码实现(以 C++ 为例)

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) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
};

 复杂度分析

        时间复杂度:由于每次循环都能将搜索区间缩小一半,最多需要进行 log_2 n 次比较,所以时间复杂度为 O(log n) ,其中 n 为数组的长度。这使得二分查找在处理大规模有序数据时表现极为出色,相比暴力遍历数组的  O(n) 时间复杂度,效率有了质的飞跃🚀!
空间复杂度:整个查找过程只需要使用几个额外的指针变量来记录索引和中间位置,无需额外的数据结构来存储数据,所以空间复杂度为 O(1) ,具有极高的空间效率👍!


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

题目链接👉【力扣】

📖题目描述

🧠讲解算法原理

        首先,借二分查找思想找到目标值任一位置😃。找首位置时,从该位置向左搜,遇到首个不等元素,其下一位即目标首位置🧐;找尾位置同理,从找到处向右搜,遇到首个不等元素,其前一位即目标尾位置。

        为避免代码重复,编写辅助函数实现二分查找核心逻辑,主函数调用它分别找目标值首尾位置😎。

💻代码实现(以 C++ 为例)

#include <vector>
using namespace std;

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        // 如果数组为空,直接返回{-1, -1},表示未找到目标值的起始和结束位置
        if(nums.size()==0) return {-1,-1}; 

        vector<int> x;
        int left = 0, right = nums.size() - 1;
        int mid = 0;

        // 查找目标值的左边界
        while(left < right) 
        {
            // 计算中间位置,防止(left + right)溢出
            mid = left+(right - left)/2; 
            if(nums[mid] >= target) 
            {
                // 如果中间值大于等于目标值,说明目标值的左边界可能在左半部分(包括mid)
                right = mid; 
            }
            if(nums[mid] < target) 
            {
                // 如果中间值小于目标值,说明目标值的左边界在右半部分
                left = mid + 1; 
            }
        }
        // 检查找到的left位置是否为目标值
        if(nums[left] == target)    
            x.push_back(left);
        else
            x.push_back(-1);

        // 重置left和right,准备查找目标值的右边界
        left = 0; right = nums.size() - 1; 
        // 查找目标值的右边界
        while(left < right) 
        {
            // 这里加1是为了保证在left和right相差1时,mid能取到right,避免死循环
            mid = left+(right - left + 1)/2; 
            if(nums[mid] > target) 
            {
                // 如果中间值大于目标值,说明目标值的右边界在左半部分
                right = mid - 1; 
            }
            if(nums[mid] <= target) 
            {
                // 如果中间值小于等于目标值,说明目标值的右边界可能在右半部分(包括mid)
                left = mid; 
            }
        }
        // 检查找到的left位置是否为目标值
        if(nums[left] == target)    
            x.push_back(left);
        else
            x.push_back(-1);

        return x;
    }
};

复杂度分析

        时间复杂度:整体时间复杂度仍为 O(log n),因为主要操作还是基于二分查找,虽然需要分别查找目标值的第一个和最后一个位置,但每次查找的时间复杂度依然是对数级别的。
        空间复杂度:同样,由于只使用了少量额外的变量来辅助查找,空间复杂度为 O(1),在空间利用上保持了高效性。


         通过对这两道题目的详细解析,我们深刻领略了二分查找算法在处理数组查找问题时的强大威力💪!它以其简洁的逻辑和高效的性能,成为算法领域中不可或缺的重要工具。希望大家在今后的算法学习过程中,能够熟练掌握并灵活运用二分查找算法,轻松应对各种类似的挑战。


 我将持续在算法领域精研深耕,为大家带来更多优质的算法知识讲解与问题剖析。

欢迎大家关注我 👉【A charmer】

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

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

相关文章

【RDMA学习笔记】1:RDMA(Remote Direct Memory Access)介绍

从帝国理工的PPT学习。 什么是RDMA Remote Direct Memory Access&#xff0c;也就是Remote的DMA&#xff0c;是一种硬件机制&#xff0c;能直接访问远端结点的内存&#xff0c;而不需要处理器介入。 其中&#xff1a; Remote&#xff1a;跨node进行数据传输Direct&#xff…

「实战应用」如何为DHTMLX JavaScript 甘特图添加进度线

DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的所有需求&#xff0c;是最完善的甘特图图表库。 今天&#xff0c;您将学习如何使用进度线补充JavaScript 甘特图&#xff0c;以便于监控项目进度。 DHTMLX Gantt 最新试用版下载 …

爬虫后的数据处理与使用(使用篇--实现分类预测)

&#xff08;&#xff09;紧接上文&#xff0c;在完成基本的数据处理后&#xff0c;接下来就是正常的使用了。当然怎么用&#xff0c;确实需要好好思考一下~ 上文&#xff1a;爬虫后的数据处理与使用&#xff08;处理篇&#xff09; 前言&#xff1a; 一般来说&#xff0c;我…

难绷,一种重命名+符号链接禁用EDR(Crowdstrike)的方法

最近看到的一个项目&#xff1a;https://github.com/rad9800/FileRenameJunctionsEDRDisable #include <windows.h>#include <winioctl.h>#include <stdio.h> typedef struct _REPARSE_DATA_BUFFER{ ULONG ReparseTag; USHORT ReparseDataLength; …

数仓建模(三)建模三步走:需求分析、模型设计与数据加载

本文包含&#xff1a; 数据仓库的背景与重要性数据仓库建模的核心目标本文结构概览&#xff1a;需求分析、模型设计与数据加载 目录 第一部分&#xff1a;需求分析 1.1 需求分析的定义与目标 1.2 需求分析的步骤 1.2.1 业务需求收集 1.2.2 技术需求分析 1.2.3 成果输出…

微信小程序-Docker+Nginx环境配置业务域名验证文件

在实际开发或运维工作中&#xff0c;我们时常需要在 Nginx 部署的服务器上提供一个特定的静态文件&#xff0c;用于域名验证或第三方平台验证。若此时使用 Docker 容器部署了 Nginx&#xff0c;就需要将该验证文件正确地映射&#xff08;挂载&#xff09;到容器中&#xff0c;并…

Python Wi-Fi密码测试工具

Python Wi-Fi测试工具 相关资源文件已经打包成EXE文件&#xff0c;可双击直接运行程序&#xff0c;且文章末尾已附上相关源码&#xff0c;以供大家学习交流&#xff0c;博主主页还有更多Python相关程序案例&#xff0c;秉着开源精神的想法&#xff0c;望大家喜欢&#xff0c;点…

usb通过hdc连接鸿蒙next的常用指令

参考官方 注册报名https://www.hiascend.com/developer/activities/details/44de441ef599450596131c8cb52f7f8c/signup?channelCodeS1&recommended496144 hdc-调试命令-调测调优-系统 - 华为HarmonyOS开发者https://developer.huawei.com/consumer/cn/doc/harmonyos-guid…

前端性能-HTTP缓存

前言 开启 HTTP 缓存是提升前端性能的常见手段之一。通过缓存&#xff0c;浏览器可以临时存储资源&#xff0c;在后续请求中直接使用本地副本&#xff0c;从而有效减少 HTTP 请求次数&#xff0c;显著缩短网页加载时间。以下是 HTTP 缓存的几个关键点&#xff1a; 1、减少重复…

【Unity-Game4Automation PRO 插件】

Game4Automation PRO 插件 是一个用于 Unity 引擎 的工业自动化仿真工具&#xff0c;它提供了对工业自动化领域的仿真和虚拟调试支持&#xff0c;特别是在与工业机器人、生产线、PLC 系统的集成方面。该插件旨在将工业自动化的实时仿真与游戏开发的高质量 3D 可视化能力结合起来…

【Linux】--- 进程的等待与替换

进程的等待与替换 一、进程等待1、进程等待的必要性2、获取子进程status3、进程等待的方法&#xff08;1&#xff09;wait&#xff08;&#xff09;函数&#xff08;2&#xff09;waitpid函数 4、多进程创建以及等待的代码模型5、非阻塞接口 轮询 二、进程替换1、替换原理2、替…

一个超快低延迟.Net网络通信库:支持TCP, SSL, UDP, HTTP,HTTPS, WebSocket多协议

今天给大家推荐一个性能好、低延迟.Net网络通信库&#xff0c;基本支持所有协议。 01 项目简介 NetCoreServer是一个基于.NET Core的开源项目&#xff0c;一个高性能、跨平台的异步套接字服务器与客户端库。该项目支持多种传输协议&#xff0c;包括TCP、SSL、UDP、HTTP、HTTP…

苍穹外卖08——(涉及接收日期格式数据、ApachePOI导出报表、sql获取top10菜品数据)

营业额统计 service层 在需要处理空值、与数据库交互或使用集合时&#xff0c;Integer 、Double是更好的选择。 // 导入string工具类 import org.apache.commons.lang.StringUtils; Service // 标记该类为Spring的服务组件 Slf4j // 引入日志功能 public class Repor…

数据结构9——二叉搜索树

&#x1f947;1.二叉搜索树的概念 二叉搜索树(Binary Search Tree,BST)又称二叉排序树或二叉查找树&#xff0c;其要么是一棵空树&#xff0c;要么具有以下性质&#xff1a; ①&#xff1a;左子树上所有节点的值都小于根节点&#xff1b; ②&#xff1a;右子树上所有节点的值都…

如何使用wireshark 解密TLS-SSL报文

目录 前言 原理 操作 前言 现在网站都是https 或者 很多站点都支持 http2。这些站点为了保证数据的安全都通过TLS/SSL 加密过&#xff0c;用wireshark 并不能很好的去解析报文&#xff0c;我们就需要用wireshark去解密这些报文。我主要讲解下mac 在 chrome 怎么配置的&…

c++ haru生成pdf输出文本实例

haru是一个开源的生成pdf的库&#xff0c;花时间终于编译成功&#xff0c;以下是一个特别简单的写文本的实例&#xff1a; #include "hpdf.h" void CDemoDlg::OnBnClickedOk() { HPDF_Error_Handler error_handler NULL; HPDF_Doc pdf; pdf HPDF_New(…

Redis与MySQL主从复制原理解析

目录 1. 介绍2. Mysql主从复制的工作原理3. Mysql复制的类型3.1 基于语句的复制&#xff08;Statement-based Replication, SBR&#xff09;3.2 基于行的复制&#xff08;Row-based Replication, RBR&#xff09;3.3 混合复制&#xff08;Mixed Replication&#xff09; 4. Red…

一步到位Python Django部署,浅谈Python Django框架

Django是一个使用Python开发的Web应用程序框架&#xff0c;它遵循MVC&#xff08;Model-View-Controller&#xff09;设计模式&#xff0c;旨在帮助开发人员更快、更轻松地构建和维护高质量的Web应用程序。Django提供了强大的基础设施和工具&#xff0c;以便于处理复杂的业务逻…

迅为RK3568开发板篇OpenHarmony配置HDF驱动控制LED-新增 topeet子系统-编写 bundle.json文件

bundle.json 文件内容如下所示&#xff1a; 下面是对各个字段的解释&#xff1a; 1. name: "ohos/demos" - 这是组件或项目的名称&#xff0c;这里表示它属于 OHOS&#xff08;OpenHarmony OS&#xff09;生态系统下的一个名为"demos"的组件。 2. descri…

STM32 物联网智能家居 (三) 输入子系统

STM32 物联网智能家居 (三) 输入子系统 下面是物联网智能家居的输入子系统&#xff0c;见下图&#xff0c;在输入子系统中会实现按键输入、网络输入、标准输入Scanf&#xff0c;其中的网络输入放入到网络子系统中进行讲解。 一、输入子系统核心功能 STM32 物联网智能家居输入…