【LeetCode 704 & 34_二分查找】二分查找 & 在排序数组中查找元素的第一个和最后一个位置

【LeetCode 704 & 34_二分查找】二分查找 & 在排序数组中查找元素的第一个和最后一个位置

场景应用

在算法学习中,二分查找是一种高效的查找算法,其时间复杂度为 O ( l o g n ) O(log n) O(logn),适用于有序数组的查找场景。在实际场景中,当只需判断目标值是否存在于有序数组中,且数组内元素唯一时,用最简单的基础二分查找就足够,比如在按学号有序排列的唯一学生ID数组中查找某学生是否存在、在无重复的商品编码有序列表中检索指定编码是否存在;而当有序数组中存在重复的目标值,且需要确定目标值的范围边界时,就需要用查找左右边界的二分查找,比如在按时间戳排序的重复打卡记录中找某员工首次和末次打卡的位置、在成绩有序数组中找某分数出现的起始和结束排名、在商品销量统计的有序数组中找某一销量值对应的首个和最后一个商品下标。

在这里插入图片描述


在这里插入图片描述



一、二分查找

1.1 题目链接

704. 二分查找【点击进入】


1.2 题目描述

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

提示

  • 你可以假设 nums 中的所有元素是不重复的。
  • n 将在 [1, 10000]之间。
  • nums 的每个元素都将在 [-9999, 9999]之间。

1.3 题目示例

示例 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 

1.4 算法思路

二分查找的核心是利用数组的有序性,不断缩小搜索范围。具体思路如下:

  1. 初始化两个指针,left 指向数组起始位置(下标为0),right 指向数组末尾位置(下标为nums.size() - 1)。
  2. left <= right 的条件下,计算中间位置 mid,计算公式为 mid = left + (right - left) / 2,该写法可避免 left + right 导致的整数溢出。
  3. 比较 nums[mid]target 的大小:
    • nums[mid] == target,找到目标值,直接返回 mid
    • nums[mid] < target,说明目标值在右半部分,将 left 更新为 mid + 1
    • nums[mid] > target,说明目标值在左半部分,将 right 更新为 mid - 1
  4. 若循环结束仍未找到目标值,返回 -1
> 此处可插入二分查找基础流程示意图

1.5 核心代码

classSolution{public:intsearch(vector<int>& nums,int target){int left =0;int right = nums.size()-1;while(left <= right){int mid = left +(right - left)/2;if(nums[mid]< target) left = mid +1;elseif(nums[mid]> target) right = mid -1;elsereturn mid;}return-1;}};

1.6 示例测试(总代码)

为了验证代码的正确性,我们可以编写测试代码,代入题目示例进行测试:

#include<iostream>#include<vector>usingnamespace std;classSolution{public:intsearch(vector<int>& nums,int target){int left =0;int right = nums.size()-1;while(left <= right){int mid = left +(right - left)/2;if(nums[mid]< target) left = mid +1;elseif(nums[mid]> target) right = mid -1;elsereturn mid;}return-1;}};intmain(){// 示例1测试 vector<int> nums1 ={-1,0,3,5,9,12};int target1 =9; Solution s;int result1 = s.search(nums1, target1); cout <<"示例1输出:"<< result1 << endl;// 预期输出4// 示例2测试 vector<int> nums2 ={-1,0,3,5,9,12};int target2 =2;int result2 = s.search(nums2, target2); cout <<"示例2输出:"<< result2 << endl;// 预期输出-1return0;}

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

2.1 题目链接

34. 在排序数组中查找元素的第一个和最后一个位置【点击进入】


2.2 题目描述

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

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

进阶:你可以设计并实现时间复杂度为 O ( l o g n ) O(log n) O(logn) 的算法解决此问题吗?

提示

  • 0 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9
  • nums 是一个非递减数组
  • -10^9 <= target <= 10^9

2.3 题目示例

示例 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] 

2.4 算法思路

本题需要找到目标值在有序数组中的第一个和最后一个位置,依然可以使用二分查找的思想,分别查找左边界和右边界:

  1. 查找左边界
    • 初始化 left = 0right = nums.size() - 1
    • 循环条件为 left < right,计算 mid = left + (right - left) / 2
    • nums[mid] < target,说明左边界在右半部分,更新 left = mid + 1;否则更新 right = mid
    • 循环结束后,检查 nums[left] 是否等于 target,若不等于则说明数组中无目标值,返回 [-1, -1]
  2. 查找右边界
    • 重新初始化 left = 0right = nums.size() - 1
    • 循环条件为 left < right,计算 mid = left + (right - left + 1) / 2(加1是为了避免死循环)。
    • nums[mid] <= target,说明右边界在右半部分,更新 left = mid;否则更新 right = mid - 1
  3. 最终返回左边界和右边界组成的数组。

查找左边界

在这里插入图片描述


查找右边界

在这里插入图片描述

2.5 核心代码

classSolution{public: vector<int>searchRange(vector<int>& nums,int target){if(nums.size()==0)return{-1,-1};int begin =0;int left =0;int 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) begin = left;elsereturn{-1,-1};//找右端点 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};}};

2.6 示例测试(总代码)

编写测试代码,验证上述核心代码的正确性:

#include<iostream>#include<vector>usingnamespace std;classSolution{public: vector<int>searchRange(vector<int>& nums,int target){if(nums.size()==0)return{-1,-1};int begin =0;int left =0;int 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) begin = left;elsereturn{-1,-1};//找右端点 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};}};intmain(){// 示例1测试 vector<int> nums1 ={5,7,7,8,8,10};int target1 =8; Solution s; vector<int> result1 = s.searchRange(nums1, target1); cout <<"示例1输出:["<< result1[0]<<","<< result1[1]<<"]"<< endl;// 预期输出[3,4]// 示例2测试 vector<int> nums2 ={5,7,7,8,8,10};int target2 =6; vector<int> result2 = s.searchRange(nums2, target2); cout <<"示例2输出:["<< result2[0]<<","<< result2[1]<<"]"<< endl;// 预期输出[-1,-1]// 示例3测试 vector<int> nums3;int target3 =0; vector<int> result3 = s.searchRange(nums3, target3); cout <<"示例3输出:["<< result3[0]<<","<< result3[1]<<"]"<< endl;// 预期输出[-1,-1]return0;}

总结

本文通过两个经典题目讲解了二分查找的基础用法和进阶应用,在实现过程中需要注意边界条件的处理,比如整数溢出、死循环等问题。二分查找的思想不仅适用于简单的元素查找,还能扩展到查找边界、寻找旋转点等场景,掌握好二分查找对算法学习至关重要。希望本文能帮助你更好地理解和运用二分查找算法。


✨ 坚持用清晰易懂的图解+代码语言, 让每个知识点都简单直观!
🚀 个人主页不呆头 · ZEEKLOG
🌱 代码仓库不呆头 · Gitee
📌 专栏系列 :📖 《C语言》🧩 《数据结构》💡 《C++》🐧 《Linux》💬 座右铭 :“不患无位,患所以立。”

Read more

clawdbot (openclaw) + discord 机器人部署指南学习教程

clawdbot (openclaw) + discord 机器人部署指南学习教程

本文介绍了基于 ClawdBot(OpenClaw)框架在 Discord 平台部署 AI 对话机器人的完整流程。内容包括:Discord Application 与 Bot 的创建配置、OAuth2 权限管理、pnpm 全局安装、Daemon 服务配置、多模型 API 接入(支持智谱 GLM 等主流大模型)、Gateway 服务启动与调试等核心环节。 一、网络要求 * 魔法 * 确保网络能够访问Discord服务 * TUN模式(关键哦) 二、Discord平台配置 2.1 访问Discord开发者平台 访问地址:https://discord.com/developers/applications 2.2 创建应用程序 1. 登录Discord开发者平台

By Ne0inhk

飞书 × OpenClaw 接入指南:不用服务器,用长连接把机器人跑起来

你想在飞书里用上一个能稳定对话、能发图/收文件、还能按规则在群里工作的 AI 机器人,最怕两件事:步骤多、出错后不知道查哪里。这个项目存在的意义,就是把“飞书接 OpenClaw”这件事,整理成一套对非技术也友好的配置入口,并把官方文档没覆盖到的坑集中写成排查清单。 先说清楚它的角色:OpenClaw 现在已经内置官方飞书插件 @openclaw/feishu,功能更完整、维护也更及时。这是好事,说明飞书 + AI 的接入已经走通。这个仓库并不是要替代官方插件,而是继续为大家提供: * 新用户:从零开始的新手教程(15–20 分钟) * 老用户:从旧版(独立桥接或旧 npm 插件)迁移到官方插件的保姆级路线 * 常见问题答疑 & 排查清单(最常见的坑优先) * 进阶场景:独立桥接模式依然可用(需要隔离/定制时再用) 另外,仓库也推荐了一个新项目

By Ne0inhk

Xilinx FPGA上构建RISC-V五级流水线CPU实战案例

以下是对您提供的博文内容进行 深度润色与结构重构后的技术文章 。整体风格更贴近一位资深嵌入式系统教学博主的自然表达:逻辑清晰、语言精炼、富有实战温度,彻底去除AI腔调和模板化痕迹;同时强化了工程细节、设计权衡与真实调试经验,使读者既能理解原理,又能照着落地。 在Xilinx FPGA上手撸一个五级流水线RISC-V CPU:不是Demo,是真能跑 addi 和 beq 的硬核实践 你有没有试过,在FPGA上跑通第一条自己写的RISC-V指令?不是用Vivado自动生成的IP核,也不是靠PicoRV32“一键导入”,而是从零开始画出IF/ID/EX/MEM/WB每一级、亲手写完所有前递逻辑、连ILA探针都打在ALU输出口上——看着波形里 pc=0x1004 跳到 0x1008 ,再看到 x1 真的被 lw 从内存里读出来、又被下一条 add 正确用了……那种感觉,比仿真通过还踏实。 这正是本文要带你完成的事: 在一个XC7A100T(Artix-7)

By Ne0inhk

如何用FPGA实现高精度无刷电机控制?从原理到落地的完整指南

如何用FPGA实现高精度无刷电机控制?从原理到落地的完整指南 【免费下载链接】FPGA-FOCFPGA-based Field Oriented Control (FOC) for driving BLDC/PMSM motor. 基于FPGA的FOC控制器,用于驱动BLDC/PMSM电机。 项目地址: https://gitcode.com/gh_mirrors/fp/FPGA-FOC 在工业自动化与机器人领域,如何突破传统MCU在电机控制中的性能瓶颈?FPGA以其并行处理架构为场定向控制(FOC)算法提供了全新的实现路径。本文将系统解析基于FPGA的无刷电机驱动技术,通过硬件-算法-工程实现的三维度分析,帮助开发者掌握FPGA电机控制的核心方法与工程落地技巧。 价值主张:为什么FPGA是电机控制的理想选择 当我们谈论高精度电机控制时,传统MCU方案往往面临三大挑战:计算能力不足导致的控制延迟、采样速率受限影响的控制精度、以及多轴扩展时的资源冲突。FPGA-FOC项目通过硬件并行架构从根本上解决了这些问题,其核心优势体现在三个维度: 硬件架构的突破 FPGA的并行处理特

By Ne0inhk