Skip to content

二分查找的三种模板

为了避免边界问题的困扰,本节提供三种固定模板,覆盖所有常见场景。


模板一:标准二分(找精确值)

typescript
function binarySearch(nums: number[], target: number): number {
  let left = 0;
  let right = nums.length - 1;
  
  while (left <= right) {
    const mid = left + Math.floor((right - left) / 2);
    
    if (nums[mid] === target) {
      return mid;
    } else if (nums[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }
  
  return -1;
}

特点

  • 区间:[left, right]
  • 终止:left > right
  • 用途:找到返回索引,找不到返回 -1

模板二:查找左边界(第一个 >= target)

typescript
function lowerBound(nums: number[], target: number): number {
  let left = 0;
  let right = nums.length;  // 注意是 length,不是 length - 1
  
  while (left < right) {
    const mid = left + Math.floor((right - left) / 2);
    
    if (nums[mid] >= target) {
      right = mid;  // mid 可能是答案,保留
    } else {
      left = mid + 1;
    }
  }
  
  return left;  // left 是第一个 >= target 的位置
}

特点

  • 区间:[left, right)
  • 终止:left === right
  • 返回:第一个 >= target 的位置(可能是 nums.length)

模板三:查找右边界(最后一个 <= target)

typescript
function upperBound(nums: number[], target: number): number {
  let left = 0;
  let right = nums.length;
  
  while (left < right) {
    const mid = left + Math.floor((right - left) / 2);
    
    if (nums[mid] <= target) {
      left = mid + 1;  // mid 不是答案,排除
    } else {
      right = mid;
    }
  }
  
  return left - 1;  // 最后一个 <= target 的位置
}

特点

  • 返回:最后一个 <= target 的位置(可能是 -1)

三种模板对比

模板区间终止条件返回值
精确查找[left, right]left > right索引或 -1
左边界[left, right)left == right第一个 >= target
右边界[left, right)left == right最后一个 <= target

如何选择模板?

需求选择模板
找精确值模板一
找第一个等于 target模板二 + 验证
找最后一个等于 target模板三 + 验证
找第一个 > target模板二,target 改为 target + 1
找最后一个 < target模板三,target 改为 target - 1
找插入位置模板二

示例:找第一个等于 target

typescript
function findFirst(nums: number[], target: number): number {
  const pos = lowerBound(nums, target);
  
  if (pos < nums.length && nums[pos] === target) {
    return pos;
  }
  return -1;
}

示例:找最后一个等于 target

typescript
function findLast(nums: number[], target: number): number {
  const pos = upperBound(nums, target);
  
  if (pos >= 0 && nums[pos] === target) {
    return pos;
  }
  return -1;
}
二分查找的三种模板 has loaded