半开区间和开区间的两个二分模版

概念先行(统一记法)

  • leftright:区间端点。

  • mid = left + (right - left) / 2mid = (left + right) >>> 1(避免溢出)。

  • n:数组长度,合法下标是 0 .. n-1

  • two styles:

    • 半开区间 [left, right):右端点不包含(常用在查找插入位置 / lower_bound / upper_bound)。
    • 闭区间 [left, right]:左右端点都包含(常用在查找是否存在某值并返回下标)。

一、半开区间([left, right))模板 ------ 推荐用于 lower_bound / 插入点

不变量(重要)

目标插入位置始终位于区间 [left, right) 中(右端点不包含)。

结构(Java)

ini 复制代码
int left = 0, right = n; // right = n -> 表示 [0, n)
while (left < right) {
    int mid = (left + right) >>> 1; // or left + (right-left)/2
    if (A[mid] < target) {
        left = mid + 1;   // 插入位置在右半区: [mid+1, right)
    } else {
        right = mid;      // 插入位置在左半区(包含 mid): [left, mid)
    }
}
// loop 结束时 left == right,即插入位置
int pos = left;

常见变体

  • lower_bound(第一个 >= target) :上面模板正是 lower_bound。

  • upper_bound(第一个 > target)

    ini 复制代码
    // 半开区间,right = n
    while (left < right) {
        int mid = (left + right) >>> 1;
        if (A[mid] <= target) left = mid + 1;
        else right = mid;
    }
    pos = left; // 第一个 > target 的下标

优点

  • 简洁、不易错的模板(左闭右开避免 mid == right 情况)。
  • 自然返回"插入位置",适合维护 tails 这类需要插入点的场景。

二、闭区间([left, right])模板 ------ 适合查找是否存在且返回下标

不变量(重要)

目标位置始终在 [left, right] 中,循环条件通常是 left <= right

结构(Java)------ 查找"是否存在并返回任意下标"

ini 复制代码
int left = 0, right = n - 1;
while (left <= right) {
    int mid = (left + right) >>> 1;
    if (A[mid] == target) return mid;
    else if (A[mid] < target) left = mid + 1;
    else right = mid - 1;
}
// not found -> left is insertion point, right = left - 1

变体:用闭区间实现 lower_bound(第一个 >= target)

ini 复制代码
int left = 0, right = n - 1;
int pos = n; // 默认插入到末尾
while (left <= right) {
    int mid = (left + right) >>> 1;
    if (A[mid] >= target) {
        pos = mid;      // candidate
        right = mid - 1; // 缩到左半区
    } else {
        left = mid + 1;
    }
}
// pos 是第一个 >= target(可能为 n 表示 append)

注意点(闭区间常见错误)

  • 当要把 mid 作为"候选"(A[mid] >= target)时,必须做 right = mid - 1,否则区间不会收缩(会死循环)。
  • 初始化 right 要是 n - 1(不是 n)。否则访问 A[mid] 会越界。

三、对比与选用建议(实用)

  • 想要插入位置 / lower_bound / upper_bound -> 用半开区间模板(更简洁、易用)。
  • 单纯判断是否存在并返回下标 -> 用闭区间模板(直观)。
  • 半开区间:left=0,right=n, while(left<right), right=mid
  • 闭区间:left=0,right=n-1, while(left<=right), right=mid-1(当 mid 作为 candidate 时)。

四、常见错误 & 排查清单

  1. 越界right 设为 n 却用 while(left<=right),会算出 mid==n → 访问 A[n] 越界。
  2. 死循环 :闭区间写 while(left<=right) 却在候选分支写 right = mid(而非 mid-1)会导致区间不收缩。
  3. off-by-one :不清楚 mid 属于哪半区,更新时要保证区间长度严格减小。
  4. mid 溢出 :用 (left+right)/2 在极端大整数下溢出,改用 left + (right - left)/2>>>1
  5. 空数组 :在半开区间 right = 0,循环不进,pos=0 正确;闭区间 right = -1,要注意 while(left<=right) 不进并处理返回值。

五、快速模板汇总(Java)

半开区间 lower_bound(第一个 >= target):

ini 复制代码
int left = 0, right = n; // [left, right)
while (left < right) {
    int mid = (left + right) >>> 1;
    if (A[mid] < target) left = mid + 1;
    else right = mid;
}
int pos = left;

半开区间 upper_bound(第一个 > target):

ini 复制代码
int left = 0, right = n;
while (left < right) {
    int mid = (left + right) >>> 1;
    if (A[mid] <= target) left = mid + 1;
    else right = mid;
}
int pos = left;

闭区间查找 exact match(是否存在):

ini 复制代码
int left = 0, right = n - 1;
while (left <= right) {
    int mid = (left + right) >>> 1;
    if (A[mid] == target) return mid;
    else if (A[mid] < target) left = mid + 1;
    else right = mid - 1;
}
return -1;

闭区间 lower_bound(第一个 >= target):

ini 复制代码
int left = 0, right = n - 1;
int pos = n;
while (left <= right) {
    int mid = (left + right) >>> 1;
    if (A[mid] >= target) { pos = mid; right = mid - 1; }
    else left = mid + 1;
}
// pos is first >= target, may be n (not found -> insert at end)

六、举例演练(半开区间找 lower_bound)

数组 A=[2,3,7,101], n=4, target=18:

  • left=0,right=4 -> mid=2(A[2]=7)<18 -> left=3
  • left=3,right=4 -> mid=3(A[3]=101)>=18 -> right=3
  • left==right==3 -> pos=3(正确,替换 101)
相关推荐
Old Uncle Tom18 小时前
OpenClaw 记忆系统 -- 记忆预加载
java·数据结构·算法·agent
会编程的土豆18 小时前
洛谷题单入门1 顺序结构
数据结构·算法·golang
生信碱移18 小时前
PACells:这个方法可以鉴定疾病/预后相关的重要细胞亚群,作者提供的代码流程可以学习起来了,甚至兼容转录组与 ATAC 两种数据类型!
人工智能·学习·算法·机器学习·数据挖掘·数据分析·r语言
智者知已应修善业18 小时前
【51单片机中的打飞机设计】2023-8-25
c++·经验分享·笔记·算法·51单片机
智者知已应修善业21 小时前
【51单片机按键调节占空比3位数码管显示】2023-8-24
c++·经验分享·笔记·算法·51单片机
.54821 小时前
## Sorting(排序算法)
python·算法·排序算法
wuweijianlove1 天前
算法的平均复杂度建模与性能回归分析的技术7
算法·数据挖掘·回归
子琦啊1 天前
【算法复习】字符串 | 两个底层直觉,吃透高频题
linux·运维·算法
code_pgf1 天前
Octo 算法详解-开源通用机器人策略模型技术报告
算法·机器人·开源
嘻嘻哈哈樱桃1 天前
牛客经典101题题解集--动态规划
java·数据结构·python·算法·职场和发展·动态规划