「算法」查找算法之二分查找

947 阅读1分钟

查找算法

  • 线性查找:一个个找;实现简单;太慢
  • 二分查找:有序;简单;插入特别慢
  • HASH:查询快;占用空间;不太适合存储大规模数据
  • 二叉查找树:插入和查询很快(log(N));无法存大规模数据,复杂度退化
  • 平衡树:解决 bst 退化的问题,树是平衡的;节点非常多的时候,依然树高很高
  • 多路查找树:一个父亲多个孩子节点(度);节点过多树高不会特别深
  • 多路平衡查找树 B-Tree

二分查找

思路 1:「直接找」

第 1 种思路比较简单,一旦我们在循环体中找到元素就直接返回结果。

  • 取两个节点中心位置 mid,先看中心位置值 nums[mid]
  • 如果中心位置值 nums[mid] 与目标值 target 相等,则 直接返回 这个中心位置元素的下标。
  • 如果中心位置值 nums[mid] 小于目标值 target,则将左节点设置为 mid + 1,然后继续在右区间 [mid + 1, right] 搜索。
  • 如果中心位置值 nums[mid] 大于目标值 target,则将右节点设置为 mid - 1,然后继续在左区间 [left, mid - 1] 搜索。
def bin_search(lst, value):
    left, right = 0, len(lst) - 1
    while left <= right:
        mid = (left + right) // 2
        if value == lst[mid]:
            return mid
        elif value < lst[mid]:
            right = mid - 1
        else:
            left = mid + 1
    return -1

思路 2:「排除法」

第 2 种思路在循环体中排除目标元素一定不存在区间。

  • 取两个节点中心位置 mid,根据判断条件先将目标元素一定不存在的区间排除。
  • 然后在剩余区间继续查找元素,继续根据条件排除不存在的区间。
  • 直到区间中只剩下最后一个元素,然后再判断这个元素是否是目标元素。
def search(lst, value):
    left, right = 0, len(lst) - 1
    # 在区间 [left, right] 内查找 target
    while left < right:
        mid = (left + right) // 2
        # lst[mid] 小于目标值,排除掉不可能区间 [left, mid],在 [mid + 1, right] 中继续搜索
        if lst[mid] < value:
            left = mid + 1
        # lst[mid] 大于等于目标值,目标元素可能在 [left, mid] 中,在 [left, mid] 中继续搜索
        else:
            right = mid
    # 判断区间剩余元素是否为目标元素,不是则返回 -1
    return left if lst[left] == value else -1

两种思路适用范围

  • 二分查找的思路 1:因为判断语句是 left <= right,有时候要考虑返回是 left 还是 right。循环体内有 3 个分支,并且一定有一个分支用于退出循环或者直接返回。这种思路适合解决简单题目。即要查找的元素性质简单,数组中都是非重复元素,且 ==>< 的情况非常好写的时候。
  • 二分查找的思路 2:更加符合二分查找算法的减治思想。每次排除目标元素一定不存在的区间,达到减少问题规模的效果。然后在可能存在的区间内继续查找目标元素。这种思路适合解决复杂题目。比如查找一个数组里可能不存在的元素,找边界问题,可以使用这种思路。

参考资料

算法通关手册(LeetCode):algo.itcharge.cn/01.数组/03.二分…