一、介绍
二分查找也称折半查找,是一种效率比较高的查找方法 二分查找指对具有指定左索引和右索引的连续序列进行操作。 定义左索引,右索引以及中间值,并比较查找目标或将查找条件应用于集合的中间值 如果条件不成立则清除不成立的那一半,继续查剩下的一部分,直到查找目标,如果没有查到,则返回-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 + 1
或right = mid - 1
,左区间也是如此
最后选择哪种,还是要根据需求拉处理。