二分查找总结

124 阅读2分钟

一、介绍

二分查找也称折半查找,是一种效率比较高的查找方法 二分查找指对具有指定左索引和右索引的连续序列进行操作。 定义左索引,右索引以及中间值,并比较查找目标或将查找条件应用于集合的中间值 如果条件不成立则清除不成立的那一半,继续查剩下的一部分,直到查找目标,如果没有查到,则返回-1

算法要求

  • 必须采用顺序存储结构
  • 必须按关键字大小排序

二分查找的三部分

  • 预处理 如果集合未排序则进行排序
  • 二分查找 使用循环或者递归,每次比较厚将空间划分为两半
  • 后处理 在剩余空间中确定可行的值

二、模板

模板一

属性

二分查找的最基础和最基本的形式。
查找条件可以直接确定
不需要后续处理

语法

初始条件: left = 0, right = length -1
终止条件:left > right
向左查找 right = mid - 1
向右查找 left = mid + 1

模板二

属性

查找条件需要访问元素的直接右值
判断元素的右值满足否来确定来向左还是向右
需要保证查找空间每步至少有两个元素
需要后续处理

语法

初始条件: left = 0, right = length
终止: left == right
向左查找:right = mid
向右查找:left = mid + 1

模板三

属性

搜索条件需要访问元素的直接左右值
使用元素的左右值来确定查找方向
确保每步骤都至少需要3个元素
需要后续处理

语法

初始条件:left = 0, right = length - 1
终止: left + 1 == right
向左查找:right = mid
向右查找:left = mid

三、 例子

模板一 搜索旋转排序数组

let search = function(nums, target) {
  let len = nums.length, left = 0 , right = len - 1
  if(len == 1 && nums[0] == target){
    return 0
  }
  while(left <= right) {
    let mid = left + Math.floor((right - left)>>1)
    let midNum = nums[mid]
    if(midNum === target) {
      return mid
    }
    if(nums[0]<= midNum) {
      if(nums[0] <= target && target < midNum) {
        right = mid - 1
      } else {
        left = mid + 1
      }
    } else {
      if(midNum < target && target <= nums[len - 1]) {
        left = mid + 1
      } else {
        right = mid - 1
      }
    }
    
  }
  return -1
}

模板二 寻找旋转排序数组中的最小值

let findMin = function(nums) {
  let len = nums.length, left = 0, right = len - 1
  while(left < right) {
    let temp = left + Math.floor((right - left) / 2)
    if (nums[temp] < nums[right]) {
      right = temp
    } else {
      left = temp + 1
    }
  }
  return nums[left]

}

模板三 寻找峰值

let findPeakElement = function(nums) {
  let len = nums.length, left = 0, right = len - 1, ind = -1
  while(left <= right) {
    let mid = Math.floor((right - left)/2)
    if(compare(nums, mid - 1, mid) == false && compare(nums, mid, mid + 1) == true) {
      ind = mid
      break
    }
    if (compare(nums, ind, ind + 1) == true) {
      right = mid - 1 
    } else {
      left = mid + 1
    }
  }
  return ind
}
// 处理边界值 
const get = (nums, ind) => {
  return (ind === -1 || ind === nums.length) == true ? [0, 0]: [1, nums[ind]]
}
// 比较的方法
const compare = (nums, ind1, ind2) => {
  const num1 = get(nums, ind1)
  const num2 = get(nums, ind2)
  // 左右边界值
  if (num1[0] !== num2[0]) return num2[0] > num1[0] ? false : true
  
  // 峰值相同得情况
  if (num1[1] === num2[1]) return false

  // 正常情况
  return num2[1] > num1[1] ? false : true
}
findPeakElement(nums)

四、总结

  • 初始条件上: 不同模板的初始条件的右索引是不相同,这个原于对需求的思考,根据不同的类型来选择
  • 其次是终止条件,终止条件上,在于是否会需求是否有重合判断的必要,有的是left<= right, 也有left + 1 < right,或者left < right
  • 移动的方式: 根据具体的需求来处理,不同类型的处理方式各部相同,一般有确定右区间的方式right = mid,移动的right = mid + 1right = mid - 1,左区间也是如此

最后选择哪种,还是要根据需求拉处理。