# 二分搜索

::: Tip 技巧

[left,right] 循环不变量: while 里面的值要符合定义的区间 mid 加减也要符合区间定义

  • 最基本的二分查找算法
  • 寻找左侧边界的二分查找
  • 寻找右侧边界的二分查找

二分算法可以用来查找任何满足同一性质的区间的边界。

每次更新区间后保证目标在搜索区间[l,r]内 :::

# 框架

int binarySearch(int[] nums, int target) {
    int left = 0, right = ...;

    while(...) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            ...
        } else if (nums[mid] < target) {
            left = ...
        } else if (nums[mid] > target) {
            right = ...
        }
    }
    return ...;
}

# 704. 二分查找 (opens new window)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */

// 基础版二分搜索 注意搜索的区间 缺点:类似[1,2,2,2,2,3] 虽然能找到目标值 2  但是无法找到第一次出现2的 而是之间位置的2索引
var search = function (nums, target) {
  let left = 0
  let right = nums.length - 1
  // [left,right]
  while (left <= right) {
    const mid = Math.floor(left + (right - left) / 2)
    if (nums[mid] === target) {
      return mid
    } else if (target > nums[mid]) {
      left = mid + 1 // [mid+1,right]
    } else if (target < nums[mid]) {
      right = mid - 1 // [left,mid-1]
    }
  }

  return -1
}

# 寻找左侧边界的二分查找

// [1,2,2,2,2,3] target 2 return 1 返回左边第一个2

var leftSearch = function (nums, target) {
  if (nums.length === 0) {
    return -1
  }
  let left = 0
  let right = nums.length - 1

  while (left <= right) {
    const mid = Math.floor(left + (right - left) / 2)
    //
    if (target > nums[mid]) {
      left = mid + 1 // 会存在越界
    } else if (target < nums[mid]) {
      right = mid - 1
    } else if (target === nums[mid]) {
      right = mid - 1
    }
  }
  // 判断是否越界了 越界这块有点模糊了
  if (left === nums.length) {
    return -1
  }

  return nums[left] === target ? left : -1
}

# 寻找右侧边界的二分查找

function rightBoundSearch(nums, target) {
  if (nums.length === 0) {
    return -1
  }
  let left = 0
  let right = nums.length - 1

  while (left <= right) {
    const mid = Math.floor(left + (right - left) / 2)
    if (target > nums[mid]) {
      left = mid + 1
    } else if (target < nums[mid]) {
      right = mid - 1
    } else if (target === nums[mid]) {
      left = mid + 1
    }
  }

  if (right < 0) {
    return -1
  }
  return target === nums[right] ? right : -1
}

# 34. 在排序数组中查找元素的第一个和最后一个位置 (opens new window)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
// 左侧二分查找
var leftSearch = function (nums, target) {
  if (nums.length === 0) {
    return -1
  }
  let left = 0
  let right = nums.length - 1

  while (left <= right) {
    const mid = Math.floor(left + (right - left) / 2)
    //
    if (target > nums[mid]) {
      left = mid + 1 // 会存在越界
    } else if (target < nums[mid]) {
      right = mid - 1
    } else if (target === nums[mid]) {
      right = mid - 1
    }
  }
  // 判断是否越界了 越界这块有点模糊了
  if (left === nums.length) {
    return -1
  }

  return nums[left] === target ? left : -1
}
// 右侧二位查找
function rightBoundSearch(nums, target) {
  if (nums.length === 0) {
    return -1
  }
  let left = 0
  let right = nums.length - 1

  while (left <= right) {
    const mid = Math.floor(left + (right - left) / 2)
    if (target > nums[mid]) {
      left = mid + 1
    } else if (target < nums[mid]) {
      right = mid - 1
    } else if (target === nums[mid]) {
      left = mid + 1
    }
  }

  if (right < 0) {
    return -1
  }
  return target === nums[right] ? right : -1
}
var searchRange = function (nums, target) {
  return [leftSearch(nums, target), rightBoundSearch(nums, target)]
}