查找算法
- 线性查找:一个个找;实现简单;太慢
- 二分查找:有序;简单;插入特别慢
- 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.二分…