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

news/2025/1/22 4:28:20/

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

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


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

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


目录

 二分查找简介

一、二分查找

📖题目描述

🧠讲解算法原理

💻代码实现(以 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);elsex.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);elsex.push_back(-1);return x;}
};

复杂度分析

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


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


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

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


http://www.ppmy.cn/news/1565133.html

相关文章

Kotlin 2.1.0 入门教程(四)

基本类型 从某种意义上说&#xff0c;一切都是对象&#xff0c;因为您可以在任何变量上调用成员函数和属性。 虽然某些类型在运行时具有优化的内部表示形式&#xff08;如数字、字符、布尔值等&#xff09;&#xff0c;但它们看起来和行为都像普通类。 即使基本类型&#xf…

PyTorch使用教程(3)-Tensor包

1、张量Tensor 张量(Tensor)是PyTorch深度学习框架中的核心数据结构&#xff0c;在PyTorch软件框架中&#xff0c;几乎所有的数据计算和信息流都是以Tensor的形式在表达。官方给出的定义是&#xff1a; 一个 torch.Tensor是一个包含单个数据类型元素的多维矩阵 关键词 单个数…

React 第二十一节 useDeferredValue 开发中用法注意事项

1、概述 useDeferredValue 是用于延迟渲染视图UI组件的&#xff0c;可以帮助我们管理视图渲染过程中的状态&#xff0c;并提高程序运行的性能&#xff1b; 2、用法 const deferredVal useDeferredValue(params)params: 是我们需要延迟的数据&#xff1b;可以是任何类型的数…

第7章:Python TDD测试Franc对象乘法功能

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…

基于springboot+thymeleaf+Redis仿知乎网站问答项目源码

项目介绍 基于springbootthymeleafRedis仿知乎网站问答项目源码&#xff0c;可以作为毕业设计项目参考学习 按照需要一定动手能力 发文章&#xff0c;发视频&#xff0c;发想法&#xff0c;提问回答&#xff0c;注册登录 开发环境 使用技术&#xff1a;springbootthymeleafRe…

【Docker】搭建一个功能强大的自托管虚拟浏览器 - n.eko

前言 本教程基于群晖的NAS设备DS423的docker功能进行搭建&#xff0c;DSM版本为 DSM 7.2.2-72806 Update 2。 n.eko 支持多种类型浏览器在其虚拟环境中运行&#xff0c;本次教程使用 Chromium​ 浏览器镜像进行演示&#xff0c;支持访问内网设备和公网地址。 简介 n.eko 是…

C/C++ 时间复杂度(On)

定义&#xff1a; 在计算机科学中&#xff0c;时间复杂性&#xff0c;又称时间复杂度&#xff0c;算法的时间复杂度是一个函数&#xff0c;它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述&#xff0c;不包括这个函数的低…

线程池遇到未处理的异常会崩溃吗?

线程池中的 execute 和 submit 方法详解 目录 引言execute 方法 使用示例代码 submit 方法 2.1 提交 Callable 任务2.2 提交 Runnable 任务 遇到未处理异常 3.1 execute 方法遇到未处理异常3.2 submit 方法遇到未处理异常 小结 引言 在多线程编程中&#xff0c;线程池是提高性…