概念先行(统一记法)
- 
left,right:区间端点。
- 
mid = left + (right - left) / 2或mid = (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 时)。
四、常见错误 & 排查清单
- 越界 :right设为n却用while(left<=right),会算出mid==n→ 访问A[n]越界。
- 死循环 :闭区间写 while(left<=right)却在候选分支写right = mid(而非mid-1)会导致区间不收缩。
- off-by-one :不清楚 mid属于哪半区,更新时要保证区间长度严格减小。
- mid 溢出 :用 (left+right)/2在极端大整数下溢出,改用left + (right - left)/2或>>>1。
- 空数组 :在半开区间 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)