常用二分法查找变形
二分法
二分法用来查找高效,快捷,时间复杂度在O(logN)。
二分法查找第一个大于X(大于等于)的数
数组a升序排列
// 在[i+1, n-1]的范围内查找第一个大于x的数的位置
int bingarySearch(int i, long long x){
if(a[n-1] <= x) return n; //都不大于x,返回n
int l = i + 1,r = n - 1;
while (l<r)
{
mid = (l+r)/2;
// 若a[mid]<=x, 说明第一个大于x的数只可能在mid后面
if(a[mid]<=x){
l = mid + 1;
}else{// 若大于,说明在mid之前(包含mid)
r = mid;
}
}
return l;
}
大于等于
if(a[mid]<x){
l = mid + 1;
}else{// 若大于,说明在mid之前(包含mid)
r = mid;
}
练习:744. 寻找比目标字母大的最小字母
二分法查找最后一个大于X(大于等于)的数
判断最后一个数是不是大于(大于等于)x,如果不是,返回n。
二分法查找最后一个小于等于x(小于)的数
int l=0,r=max;
while (l < r) {
//取高位,取地位会遇见死循环
int mid=(l+r+1)/2;
if(k>=dp[mid])l=mid;
else r=mid-1;
}
小于
if(k>dp[mid])l=mid;
else r=mid-1;
练习:35. 搜索插入位置
二分法查找第一个小于x(小于等于)的数
判断第一个数是不是小于(小于等于)x,如果是,那就是 a[0],否则不存在。
死循环问题
数据: int[] num = {1,2,3,4,5,6,7,8,9}, 查找小于或者等于8的最小值。
如上图,此时左坐标是0, 右是8, 那么
mid = (0 + 8) / 2 = 4,num[mid] = num [4] <= k, ,向右找结果,所以有 l = mid,开始下一个循环。
此时的mid = 6, num[6] = 7;
num[mid] <= k, 向右找 l = 6
下一步:
可以得到 l = 6, r = 8, mid = (6+8) / 2 = 7,a[7] = 8 <= k,那么有l = mid = 7, r = 8,
推到得到mid = 7, 问题来了上一步的时候mid已经是7了,结果会使得mid一直是7,一直循环下去。
所以如果我们求的是二分法求小于或者等于k的最大值的话,我们mid 必须取得中值的上界。
参考:(图文)二分查找,查指定值、小于或等于k的最大值,大于或等于k的最大值
二分法查找最后一个(第一个等于)x的数
def bsearch_right(nums,target):
'''求最后一个等于定值的'''
low = 0
higt = len(nums) -1
while low <= higt:
mid = low + ((higt- low) >>1 )
if nums[mid] > target:
high = mid - 1
elif nums[mid] < target:
low = mid +1
else:
if mid == (len(nums) -1) or nums[mid] != nums[mid+1]:
return mid
else:
low = mid +1 # 将区间往后移
return -1
第一个
def bsearch_left(nums,target):
'''求第一个等于定值 '''
low = 0
high = len(nums) - 1
# 这里需要 <=
while low <= high:
# 这里需要注意: 就是使用((high - low) >> 1)需要双扩号
mid = low + ((high - low) >> 1)
if nums[mid] < target:
low = mid + 1
elif nums[mid] > target:
high = mid - 1
else:
if mid == 0 or nums[mid-1] != target:
return mid
else:
high = mid -1
return -1