以下为原创代码,可以参考,但禁止转载!
@ author = yhr
一. 普通二分查找
(二分查找一定建立于有序数组上,以升序为例)
图 来自 牛客网 -王清楚,稍有改动
标记 low =0 (left) ; high = len(数组)-1 = right
普通二分法伪代码
# @ author = yhr
while low <= high:
mid = (low+high)//2
if v <a[mid]:
# 去左半部分找
high = mid -1
elif a[mid] < v:
# 去右半部分找
low = mid + 1
elif a[mid] == v:
# 找到了一个
return mid
return -1 # 真没找到
普通二分法的一些个人观点:
【我自己琢磨的,说不出来这个味,不对就打我吧。不确保绝对正确,但我感觉是正确的,大家可以一起琢磨琢磨,如果有数学证明就更好了!】
A. 如果查找成功,一定在 mid位置, 此时low可能<high。也可能low=high=mid。
B. 如果查找失败,最后此数一定在(low-1 , low)之间。
B.1 查找失败情况一:最终当low=high=mid时,如果v<a[mid],那么high-=1,此时v一定在 a[high]=a[low-1] 与 a[low]之间。
B.2 查找失败情况二:最终当 low=high=mid时,如果a[mid]<v,那么low+=1,此时v一定在 a[high]=a[low-1] 与 a[low]之间。
这个性质就可以用到该题上:
二. 本题 – 我的解题思路
题目描述:
请实现有重复数字的升序数组的二分查找。 输出在数组中 第一个 大于等于 查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
(输出位置从1开始计算)
两个主关键点: 1. 第一个 2. 大于等于
一个次关键点: 3. 输出位置从1开始计算
这里就忽略意外情况,不做细说:比如数组为空!
刚才我说,普通二分查找里,如果找不到数值v,最后他一定是在a[low-1] 与 a[low]之间!
主关键点: 1. 第一个 2. 大于等于他的数的位置,那么我们可以把大于等于统统归咎于一个情况:
在数组中,找到1. 第一个, 2.稍微大于v的位置
什么意思呢?
-
我们把数组里所有等于v的数 都看成:v + 正无穷小,即:数组里没有等于v的数,但是 有永远比他只大一点的数。
-
那么这个题目就变成 普通二分查找里,永远找不到 的情况。
-
最后 a[low] 一定是比他大的(包括只大正无穷小的情况)第一个数的位置。
那么只需要把普通二分查找中,a[mid]==v的情况 --> 变成同a[mid]>v情况一起处理即可。
于是代码可以如此处理:
#
# 二分查找
# @param n int整型 数组长度
# @param v int整型 查找值
# @param a int整型一维数组 有序数组
# @return int整型
#
class Solution:
def upper_bound_(self , n , v , a ):
if len(a) ==0:
# 处理数组为空的情况
return 1
low = 0
high = n-1
while low <=high:
mid = (low+high)//2
if v <= a[mid]:
high = mid-1
elif v > a[mid]:
low=mid +1
return low +1
但是,还有些情况,我们可以直接写出来,避免代码多跑些无用功,比如:
a. 当 v 小于 整个 升序数组
b. 当 v 大于 整个 升序数组
#
# 二分查找
# @param n int整型 数组长度
# @param v int整型 查找值
# @param a int整型一维数组 有序数组
# @return int整型
#
class Solution:
def upper_bound_(self , n , v , a ):
if len(a) ==0:
# 处理数组为空的情况
return 1
low = 0
high = n-1
加入两种特殊情况的处理工作,避免代码多跑无用功。
# 加入两个特殊情况
if v < a[low]:
# 输出位置从1开始计算
return 1
if v > a[high]:
# 输出位置从1开始计算
return high+1+1
while low <=high:
mid = (low+high)//2
if v <= a[mid]:
high = mid-1
elif v > a[mid]:
low=mid +1
# 输出位置从1开始计算
return low +1