二分查找
问:
- 存在一个有序数组,如何找出某个数
- 存在一个有序数组,如何找出大于等于某个数的最左侧位置
- 存在一个所有元素都不相等的无序数组arr,假定若arr[i-1] < arr[i] < arr[i+1],则i位置称为局部最小。若i = 0 则只需要比 arr[1] 小 , i = arr.length - 1 则只需要比 arr[arr.length - 2]小。如何找到一个局部最小的值
解:
- 基本二分查找 时间复杂度O(logN)
const arr = [1, 3, 5, 7, 9, 11, 13, 15]
const target = 3
let left = 0
let right = arr.length - 1
function demo(left, right) {
if (left > right) return false
const midIdx = Math.ceil((right + left) / 2)
if (target === arr[midIdx]) {
return midIdx
}
target > arr[midIdx] ? left = midIdx + 1 : right = midIdx - 1
return demo(left, right)
}
- 按上述思路,我们在查询到等于target的下标时,用变量resIdx去保存这个下标。同时,由于是为了找寻最左侧位置,所以我们将right左移一个位置,继续递归,如果下次查询到等于target的下标小于resIdx,我们就将resIdx重新赋值,直到递归结束。
const arr = [1,2,3,3,3,4,4,4,4,4,5,5,5,5,5,5,5,5,7,7,8]
const target = 6
let left = 0
let right = arr.length - 1
let resIdx = null
function demo(left, right) {
if (left > right) return false
const midIdx = Math.ceil((right + left) / 2)
if (target === arr[midIdx] && (!resIdx || resIdx > midIdx)) {
resIdx = midIdx
right = midIdx - 1
}
target > arr[midIdx] ? left = midIdx + 1 : right = midIdx - 1
return demo(left, right)
}
3.一般来说二分查找适用于有序数组,但是某些特定条件下也适用于无序数组。在这个问题中,我们先判断arr[0]和arr[arr.length-1]是否为局部最小,若不是,则说明数组在[0-1]区间递减,且在[arr.length-2, arr.length-1]区间递增,也就是说在坐标系上是一个U型,那么必定存在一个局部最小的值。接下来按照二分查找,不断缩减U型的区间,直到递归结束。
const arr = [9,8,7,6,10,12,5,2,1,3]
let left = 0
let right = arr.length - 1
let resIdx = null
function demo(left, right) {
if (left > right) return false
// 0 和 1懒得再做判断了,无视之
if (arr.length <= 1) return false
if (arr[0] < arr[1]) {
resIdx = 0
return true
}
if (arr[arr.length -1] < arr[arr.length - 2]) {
resIdx = arr.length - 1
return true
}
// 走到这一步,说明left - right 是个U型
const midIdx = Math.ceil((right + left) / 2)
if (arr[midIdx-1] > arr[midIdx] && arr[midIdx + 1] > arr[midIdx]) {
resIdx = midIdx
return true
}
// 这里表明 left - midIdx 是个U型
if (arr[midIdx -1] < arr[midIdx]) {
right = midIdx - 1
return demo(left, right)
}
// 这里表明midIdx - right 是个U型
if (arr[midIdx -1] > arr[midIdx]) {
left = midIdx + 1
return demo(left, right)
}
}