算法学习记录(二)

113 阅读2分钟

二分查找

问:

  1. 存在一个有序数组,如何找出某个数
  2. 存在一个有序数组,如何找出大于等于某个数的最左侧位置
  3. 存在一个所有元素都不相等的无序数组arr,假定若arr[i-1] < arr[i] < arr[i+1],则i位置称为局部最小。若i = 0 则只需要比 arr[1] 小 , i = arr.length - 1 则只需要比 arr[arr.length - 2]小。如何找到一个局部最小的值

解:

  1. 基本二分查找 时间复杂度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)
    }
  1. 按上述思路,我们在查询到等于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)
        }
    }