跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

二分算法实战:查找有序数组中元素的首尾位置

二分算法适用于解空间具有二段性的问题。通过两个经典案例,演示了如何在有序数组中查找元素的首尾位置及区间计数。重点讲解了左右边界查找的模板差异,特别是 mid 取整技巧对避免死循环的影响,以及端点合法性校验的重要性。掌握这些细节能有效规避常见错误,提升代码鲁棒性。

萤火微光发布于 2026/3/27更新于 2026/10/2581 浏览
二分算法实战:查找有序数组中元素的首尾位置

前言

当解空间具有'二段性'时,二分法往往是突破口。核心思路是根据区间中点判断答案落在哪一侧,然后舍弃一半区间继续查找。时间复杂度通常为 O(logN)。

STL 提供了现成工具:lower_bound 返回大于等于 x 的最小元素迭代器,upper_bound 返回大于 x 的最小元素迭代器,两者均为 O(log N)。理解底层逻辑比单纯调用 API 更重要。

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

题目描述

给定一个升序排列的整数数组 nums 和一个目标值 target,找出该目标值在数组中的开始位置和结束位置。如果不存在,返回 [-1, -1]。

问题示意图

原理分析

标准二分只能找到任意一个匹配项。要确定范围,需要分别寻找左边界和右边界。

  1. 定义左右指针:指向数组头尾。
  2. 二分区间收缩:根据中点值与目标值的关系移动指针。
  3. 验证端点合法性:防止越界或找不到目标值。

注意死循环陷阱:区间缩小条件及 mid 取整方式。查找左边界时通常向下取整,查找右边界时通常向上取整,避免 left = mid 导致无法收敛。

代码实现

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if (nums.empty()) return {-1, -1};
        
        // 二分查找左端点
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            if (nums[mid] >= target) right = mid;
            else left = mid + 1;
        }
        if (nums[left] != target) return {-1, -1};
        int retLeft = left;
        
        // 二分查找右端点
        left = 0; right = nums.size() - 1;
        while (left < right) {
            int mid = (left + right + 1) / 2; // 向上取整避免死循环
            if (nums[mid] <= target) left = mid;
            else right = mid - 1;
        }
        return {retLeft, right};
    }
};

二、牛可乐和魔法封印

题目描述

给定一个长度为 n 的升序数组 a,进行 q 次查询。每次查询给出两个整数 x 和 y,求数组中有多少个元素满足 $x \le a[i] \le y$。

问题示意图

原理分析

这本质上是区间计数问题。只要找到第一个大于等于 x 的位置(左端点)和最后一个小于等于 y 的位置(右端点),数量即为 right - left + 1。若找到的端点不合法(如左端点值大于 y 或右端点值小于 x),则说明区间内无元素。

代码实现

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
typedef long long LL;
LL a[N];
int n;

int binary_search(int x, int y) {
    LL l = 1, r = n;
    // 查找左端点
    while (l < r) {
        LL mid = (l + r) / 2;
        if (a[mid] >= x) r = mid;
        else l = mid + 1;
    }
    LL retl = l;
    if (a[l] < x) return 0;
    
    // 查找右端点
    l = 1; r = n;
    while (l < r) {
        LL mid = (l + r + 1) / 2;
        if (a[mid] <= y) l = mid;
        else r = mid - 1;
    }
    if (a[r] > y) return 0;
    
    return r - retl + 1;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    int q;
    cin >> q;
    while (q--) {
        int x, y;
        cin >> x >> y;
        int ret = binary_search(x, y);
        cout << ret << endl;
    }
    return 0;
}

总结

二分查找的核心在于把握'二段性'。无论是找单个值还是区间范围,关键在于如何定义收缩规则以避免死循环。多练习不同场景下的模板变体,能显著提升解题效率。

目录

  1. 前言
  2. 一、在排序数组中查找元素的第一个和最后一个位置
  3. 题目描述
  4. 原理分析
  5. 代码实现
  6. 二、牛可乐和魔法封印
  7. 题目描述
  8. 原理分析
  9. 代码实现
  10. 总结

更多推荐文章

查看全部
  • Deepyr 在 OpenHarmony 上的适配与实战指南
  • CATE 条件平均处理效应估计:五大方法原理详解与模拟数据实战
  • 动态规划专题:回文串问题与区间 DP
  • C++ 核心特性详解:命名空间、重载与引用实战
  • Python warnings 库底层机制与企业级 API 演进实战
  • Spring Boot 数据可视化与图表集成实战
  • 企业微信群机器人 Webhook 配置与消息发送流程
  • GitHub Copilot 学生认证指南:合规申请与常见问题解析
  • 微信支付接入密码输入后转圈失败问题排查
  • Java 基础:char、String、StringBuilder 与 StringBuffer 核心解析
  • AI 大模型全面概述
  • VSCode Copilot 配置文件提示未知工具警告
  • 递归与回溯算法实战:汉诺塔、链表操作及快速幂
  • AI 产品经理入门指南:核心技能与常见误区解析
  • 字节跳动音视频前端一面面经与深度解析
  • AI 编程工具深度对比:Cursor、Copilot、Trae 与 Claude Code
  • 基于 Unity 和 AI 零代码制作小游戏“飞翔的牛马”
  • 数据结构:双向链表实现与算法分析
  • 大模型产品经理转型指南:从零开始的全面路径
  • AI 时代打造超级个体:个人效能提升与实战路径

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online