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

概念先行(统一记法)

  • 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)
相关推荐
逻辑留白陈4 小时前
Adaboost进阶:与主流集成算法对比+工业级案例+未来方向
算法
Learn Beyond Limits5 小时前
Mean Normalization|均值归一化
人工智能·神经网络·算法·机器学习·均值算法·ai·吴恩达
天选之女wow5 小时前
【代码随想录算法训练营——Day28】贪心算法——134.加油站、135.分发糖果、860.柠檬水找零、406.根据身高重建队列
算法·leetcode·贪心算法
Gohldg5 小时前
C++算法·贪心例题讲解
c++·数学·算法·贪心算法
远远远远子5 小时前
类与对象 --1
开发语言·c++·算法
Aaplloo5 小时前
【无标题】
人工智能·算法·机器学习
西望云天5 小时前
The 2024 ICPC Asia Nanjing Regional Contest(2024南京区域赛EJKBG)
数据结构·算法·icpc
10岁的博客6 小时前
容器化安装新玩法
算法
不会算法的小灰6 小时前
HTML简单入门—— 基础标签与路径解析
前端·算法·html
flashlight_hi7 小时前
LeetCode 分类刷题:1901. 寻找峰值 II
python·算法·leetcode