二分查找也称为折半查找,要求查找的对象是顺序排列的(从小到大或者从大到小),其时间复杂度为O(log2n),下面是二分查找最简单的例子:
def binary_search(data_list, val):
low = 0 # 最小数下标
high = len(data_list) - 1 # 最大数下标
while low <= high:
mid = (low + high) // 2 # 中间数下标
if data_list[mid] == val: # 如果中间数下标等于val, 返回
return mid
elif data_list[mid] > val: # 如果val在中间数左边, 移动high下标
high = mid - 1
else: # 如果val在中间数右边, 移动low下标
low = mid + 1
return # val不存在, 返回None
二分法可谓是一种非常基本的查找方法,有很多场景都可以使用到二分法,下面我们来看一下。
1. 有重复元素的二分法查找
给定一个 元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的第一个出现的target,如果目标值存在返回下标,否则返回 -1。
def search(nums , target ):
res = -1
if len(nums) < 1:
return res
low, high = 0, len(nums) - 1
while low <= high:
mid = (low+ high) // 2
if nums[mid] == target:
res = mid
high = mid - 1 # 与传统二分法相比,多了该步骤,因为需要查找到第一次出现target元素的位置
elif nums[mid] > target:
high = mid - 1
else:
low = mid + 1
return res
2. 求平方根
def sqrt(self , x ):
if x < 1:
return 0
elif x < 4:
return 1
l, h = 1, x // 2
while l <= h:
mid = (l + h) // 2
if mid * mid == x:
return mid
elif mid * mid < x:
l = mid + 1
else:
h = mid - 1
return h
3. 旋转过的有序数组中寻找目标值
给定一个整数数组nums,按升序排序,数组中的元素各不相同。
nums数组在传递给search函数之前,会在预先未知的某个下标 t(0 <= t <= nums.length-1)上进行旋转,让数组变为[nums[t], nums[t+1], ..., nums[nums.length-1], nums[0], nums[1], ..., nums[t-1]]。
比如,数组[0,2,4,6,8,10]在下标3处旋转之后变为[6,8,10,0,2,4]
现在给定一个旋转后的数组nums和一个整数target,请你查找这个数组是不是存在这个target,如果存在,那么返回它的下标,如果不存在,返回-1。
"""
与中间值比较,分前半段有序和后半段有序分别判断。比有序的二分查找多一些判定条件。
1、 要么前半段有序,要么后半段有序。
2、 当前半段有序时:即循环数组中间值比循环数组最左边值大 则 nums[left] <= nums[mid]
当target 比中间值小,比最左值(前半段最小值)大时,肯定在前面部分 则high = mid - 1 。
如果不在前半段,可能在后半段,low = mid + 1。
3、同理后半段也一样。
ps:分前半段有序后半段有序时,当nums[left] <= nums[mid] 则 left到mid有序。
否则 mid到right 有序。
"""
def search(self , nums , target ):
# write code here
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if target == nums[mid]:
return mid
if nums[mid] >= nums[l]:
if (nums[mid] > target):
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target:# <= nums[r]:
l = mid + 1
else:
r = mid - 1
return -1
网友评论