基于python的二分搜索和例题
二分搜索
二分概念
二分搜索是一种在有序数组中查找某一特定元素的搜索算法。
搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;
如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。
如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。

注意
二分搜索或者说二分查找应该注意的几个点:
- 区间的开闭:左闭右闭,还是左闭右开
- 循环条件:left<right,还是left<=right,实际和第一点是呼应的
- 更新条件:左右区间的更新,同样要对应开闭原则,维持区间的循环不变性
- 返回值:返回left或right,还是需要再判断
- 搜索条件:大于,等于,小于,大于等于,小于等于
例题
以下是一些二分搜索的一些例题,请细品!
01. 在旋转之后的列表查找最小值
假设有一个升序排列的数列在某个未知节点处被前后调换,请找到数列中的最小值
例如:[4, 5, 6, 7, 1, 2, 3]
def search_min(li):
if len(li) == 0:
return -1 left, right = 0, len(li) - 1
while left + 1 < right:
# 左边小于右边,证明有序直接返回左边元素
if li[left] < li[right]:
return li[left] mid = left + (right - left) // 2
if li[mid] > li[left]:
left = mid + 1
else:
right = mid return li[left] if li[left] < li[right] else li[right] li= [4, 5, 6, 7, 1, 2, 3]
print(search_min(li)) #
02. 旋转列表查找数值
假设有一个升序排列的数列在某个未知节点处被前后调换,请找到数列中的item
例如:在[4, 5, 6, 7, 8, 1, 2, 3] 找 1 返回index 5
def search_item(li, item):
if len(li) == 0:
return -1 left, right = 0, len(li)-1
while left+1 < right:
mid = left + (right-left) //2 if li[mid] == item:
return mid if li[mid] > li[left]:
if li[mid] >= item and li[left] <= item:
right = mid
else:
left = mid
else:
if li[mid] <= item and li[right] >= item:
left = mid
else:
right = mid if li[left] == item:
return left
elif li[right] == item:
return right
else:
return -1 li= [4, 5, 6, 7, 1, 2, 3]
print(search_item(li, 3)) #
03. 搜索插入位置
给定有序数组和一个目标值,如果在数组中找到此目标值则返回目标值的index,
如果没有找到,则返回目标值按顺序应该被插入的位置index.
注:可以假设数组中不存在重复数。
def search_insert_position(li, item):
if len(li) == 0:
return 0 left, right = 0, len(li) - 1
while left + 1 < right:
mid = left + (right - left) // 2
if li[mid] == item:
return mid if li[mid] < item:
left = mid
else:
right = mid if li[left] >= item:
return left
if li[right] >= item:
return right return right+1 li= [1, 2, 3, 4, 5, 6, 7]
print(search_insert_position(li, 3)) #
04. 搜索区间
找给定的目标值的开始和结束的位置
例如 给定列表[1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 7, 8, 9], 目标值5,返回(6,9)
def search_range(li, item):
if len(li) == 0:
return -1, -1 # 搜索左边边界
left, right = 0, len(li) - 1
while left + 1 < right:
mid = left + (right - left) // 2
if li[mid] == item:
right = mid
elif li[mid] < item:
left = mid
else:
right = mid if li[left] == item:
left_bound = left
elif li[right] == item:
left_bound = right
else:
return -1, -1 # 搜索右边边界
left, right = 0, len(li) - 1
while left + 1 < right:
mid = left + (right - left) // 2
if li[mid] == item:
left = mid
elif li[mid] < item:
left = mid
else:
right = mid if li[right] == item:
right_bound = right
elif li[left] == item:
right_bound = left
else:
return -1, -1 return left_bound, right_bound li = [1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 7, 8, 9]
print(search_range(li, 5)) # (6, 9)
05. 再有空字符串的列表中查找数值
给定一个有序的字符串序列,这个序列中的字符串用空字符隔开,请写出找到给定字符串位置的方法
例如:["", "", 1, "", 3, 4, "", "", "", 5, "", 6, 7, 8, "", ""]
def search_item(li, item):
if len(li) == 0:
return -1 left, right = 0, len(li) - 1 while left + 1 < right:
# 去除右边的空字符串
while left + 1 < right and li[right] == "":
right -= 1 if li[right] == "":
right -= 1 if right < left:
return -1 # 获取mid
mid = left + (right - left) // 2
while li[mid] == "":
mid += 1 if li[mid] == item:
return mid if li[mid] < item:
left = mid + 1
else:
right = mid - 1 if li[left] == item:
return left
elif li[right] == item:
return right
else:
return -1 li = ["", "", 1, "", 3, 4, "", "", "", 5, "", 6, 7, 8, "", ""]
print(search_item(li, 5)) #
06. 查找某个元素第一次出现的位置
在数据流中常用, 不知道列表长度
例如:[0, 1, 2, 3, 4, 4, 4, 5, 5, 6, 7, 8, 9] 查找5, 返回index 7
def search_first(li, item):
left, right = 0, 1 while li[right] < item:
left = right
right *= 2 if right > len(li):
right = len(li) - 1
break while left + 1 < right:
mid = left + (right - left) // 2 if li[mid] == item:
right = mid
elif li[mid] < item:
left = mid
else:
right = mid if li[left] == item:
return left
elif li[right] == item:
return right
else:
return -1 li = [0, 1, 2, 3, 4, 4, 4, 5, 5, 6, 7, 8, 9]
print(search_first(li, 5)) #
07. 供暖设备
冬季来临!你的首要任务是设计一款有固定供暖半径的供暖设备来给所有的房屋供暖。
现在你知道所有房屋以及供暖设备在同一水平线上的位置分布,请找到能给所有房屋供暖的供暖设备的最小供暖半径。
你的输入是每个房屋及每个供暖设备的位置,输出应该是供暖设备的最小半径。
from bisect import bisect # 找到可以插入的位置
def search_insert_position(li, item):
if len(li) == 0:
return 0 left, right = 0, len(li) - 1
while left + 1 < right:
mid = left + (right - left) // 2
if li[mid] == item:
return mid if li[mid] < item:
left = mid
else:
right = mid if li[left] >= item:
return left
if li[right] >= item:
return right return right + 1 def findRadius(houses, heaters):
# 时间复杂度(nlogn)
heaters.sort()
ans = 0
for house in houses:
# 查找房子可插入供暖设备中的位置, 返回index
# index = bisect(heaters, h)
index = search_insert_position(heaters, house)
# 左边界大于等于0,否则为负无穷
left = heaters[index - 1] if index - 1 >= 0 else float('-inf')
right = heaters[index] if index < len(heaters) else float('inf')
# 先求房子与每一个供暖设备距离的最小值,再求其中的最大值
ans = max(ans, min(house-left, right-house))
return ans houses = [1,2,3,6]
heaters = [1,4]
print(findRadius(houses, heaters)) #
08. 求平方根
例如 输入40,输出6, 6*6<40<7*7
def sqrt(item):
if item == 0:
return 0 left, right = 1, item
while left <= right:
mid = left + (right-left) // 2
if item // mid == mid:
return mid if item // mid > mid:
left = mid + 1
else:
right = mid -1 return right print(sqrt(80)) #
09. 找重复数
给定一个包含n+1个整数的数组,其中每个元素为1到n闭区间的整数值,请证明至少存在一个重复数。
假设只有一个重复数,请找到这个重复数。
def find_duplicate(li):
left = 0
right = len(li) - 1 while left < right:
mid = left + (right - left) // 2
count = 0
for i in li:
if i <= mid:
count += 1
if count <= mid:
left = mid + 1
else:
right = mid return left li = [3, 5, 6, 3, 1, 4, 2]
print(find_duplicate(li)) #
10. 合并区间
给定一个区间的集合,将所有存在交叉范围的区间进行合并。
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
说明: 因为区间 [1,3] 和 [2,6] 存在交叉范围, 所以将他们合并为[1,6]
# 定义区间
class Interval:
def __init__(self, s=0, e=0):
self.start = s
self.end = e def __str__(self):
return f"[{self.start}, {self.end}]" def __repr__(self):
return "[%s, %s]" % (self.start, self.end) def merge(intervals):
# 区间按照第一个元素排序
intervals.sort(key=lambda x: x.start) # 定义一个新的列表存放区间
merged = []
for interval in intervals:
# 如果合并区间列表为空或当前区间与上一个区间不重叠,只需附加它即可。
if not merged or merged[-1].end < interval.start:
merged.append(interval)
else:
# 有重叠将当前区间和上一个区间合并
merged[-1].end = max(merged[-1].end, interval.end) return merged intervals1 = Interval(1, 3)
intervals2 = Interval(2, 6)
intervals3 = Interval(8, 10)
intervals4 = Interval(15, 18)
intervals = [intervals1, intervals2, intervals3, intervals4]
print(merge(intervals)) # [[1, 6], [8, 10], [15, 18]]
11. 插入区间
给定一个没有交叉范围的区间集合,在这个集合中插入一个新的区间(如果需要,请进行合并)。
你可以认为这些区间已经初始时根据他们的头元素进行过排序
输入:区间集合=[[1,3],[6,9]], 新区间 = [2,5]
输出:[[1,5],[6,9]]
# 定义区间
class Interval:
def __init__(self, s=0, e=0):
self.start = s
self.end = e def __str__(self):
return f"[{self.start}, {self.end}]" def __repr__(self):
return "[%s, %s]" % (self.start, self.end) def insert(intervals, newInterval):
merged = []
for i in intervals:
if newInterval is None or i.end < newInterval.start:
merged += i,
elif i.start > newInterval.end:
merged += newInterval,
merged += i,
newInterval = None
else:
newInterval.start = min(newInterval.start, i.start)
newInterval.end = max(newInterval.end, i.end)
if newInterval is not None:
merged += newInterval,
return merged intervals1 = Interval(1, 3)
intervals2 = Interval(6, 9)
intervals = [intervals1, intervals2]
new = Interval(2, 5)
print(insert(intervals, new)) # [[1, 5], [6, 9]]
解法..
# 定义区间
class Interval:
def __init__(self, s=0, e=0):
self.start = s
self.end = e def __str__(self):
return f"[{self.start}, {self.end}]" def __repr__(self):
return "[%s, %s]" % (self.start, self.end) def insert2(intervals, newInterval):
if len(intervals) == 0:
intervals += newInterval, startPos = searchPosition(intervals, newInterval.start)
endPos = searchPosition(intervals, newInterval.end) newStart = 0 # case 1:
# startPos
# A
# |____| |____| |____|
# <-
# startPos is less than A
# and intervals[startPos].end >= newInterval.start
# then
# new A
# |____| |____| |____|
# <-
# newInterval starts within ONE interval
# so newStart = intervals[startPos].start
if (startPos >= 0 and intervals[startPos].end >= newInterval.start):
newStart = intervals[startPos].start
else:
# case 2:
# startPos = -1
# A
# |____| |____| |____|
# newInterval starts before 1st interval
# so newStart = newInterval.start # case 3:
# startPos >= 0
# A B
# |____| |____| |____|
# newInterval starts between A and B
# so NOT intervals[startPos].end >= newInterval.start
# so newStart = newInterval.start
newStart = newInterval.start
startPos += 1 newEnd = 0
# case 1:
# endPos >= 0
# endPos
# A
# |____| |____| |____|
# <-
# endPos is less than A
# so newEnd = Math.max(newInterval.end, intervals.get(endPos).end)
if (endPos >= 0):
newEnd = max(newInterval.end, intervals[endPos].end)
else:
# case 2:
# endPos < 0
# endPos
# A
# |____| |____| |____|
#
# endPos is before 1st interval
# create a new interval
newEnd = newInterval.end for i in range(startPos, endPos + 1):
intervals.pop(startPos) # note: NOT i, but startPos, since one element is removed. intervals.insert(startPos, Interval(newStart, newEnd))
return intervals # return (actual insertion position - 1)
def searchPosition(intervals, x):
start = 0
end = len(intervals) - 1
while (start <= end):
mid = start + (end - start) // 2
if (intervals[mid].start == x):
return mid
if (intervals[mid].start < x):
start = mid + 1
else:
end = mid - 1 return end
2
~>.<~
基于python的二分搜索和例题的更多相关文章
- 基于python的分治法和例题
分治法 分治法的核心 分:将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题 治:最后的子问题,可以很容易的直接求解 合:所有子问题的解合并起来就是原问题的解 分治法的特征 ...
- 【Machine Learning】决策树案例:基于python的商品购买能力预测系统
决策树在商品购买能力预测案例中的算法实现 作者:白宁超 2016年12月24日22:05:42 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本 ...
- 基于Python+Django的Kubernetes集群管理平台
➠更多技术干货请戳:听云博客 时至今日,接触kubernetes也有一段时间了,而我们的大部分业务也已经稳定地运行在不同规模的kubernetes集群上,不得不说,无论是从应用部署.迭代,还是从资源调 ...
- 关于《selenium2自动测试实战--基于Python语言》
关于本书的类型: 首先在我看来技术书分为两类,一类是“思想”,一类是“操作手册”. 对于思想类的书,一般作者有很多年经验积累,这类书需要细读与品位.高手读了会深有体会,豁然开朗.新手读了不止所云,甚至 ...
- psutil一个基于python的跨平台系统信息跟踪模块
受益于这个模块的帮助,在这里我推荐一手. https://pythonhosted.org/psutil/#processes psutil是一个基于python的跨平台系统信息监视模块.在pytho ...
- 一次完整的自动化登录测试-基于python+selenium进行cnblog的自动化登录测试
Web登录测试是很常见的测试!手动测试大家再熟悉不过了,那如何进行自动化登录测试呢!本文作者就用python+selenium结合unittest单元测试框架来进行一次简单但比较完整的cnblog自动 ...
- 搭建基于python +opencv+Beautifulsoup+Neurolab机器学习平台
搭建基于python +opencv+Beautifulsoup+Neurolab机器学习平台 By 子敬叔叔 最近在学习麦好的<机器学习实践指南案例应用解析第二版>,在安装学习环境的时候 ...
- 《Selenium2自动化测试实战--基于Python语言》 --即将面市
发展历程: <selenium_webdriver(python)第一版> 将本博客中的这个系列整理为pdf文档,免费. <selenium_webdriver(python)第 ...
- 从Theano到Lasagne:基于Python的深度学习的框架和库
从Theano到Lasagne:基于Python的深度学习的框架和库 摘要:最近,深度神经网络以“Deep Dreams”形式在网站中如雨后春笋般出现,或是像谷歌研究原创论文中描述的那样:Incept ...
随机推荐
- AT2377 Blue and Red Tree
AT2377 Blue and Red Tree 法一:正推 红色的边在蓝色的树上覆盖,一定每次选择的是覆盖次数为1的边的覆盖这条边的红色边连出来 覆盖次数可以树剖找到 这条红色边,可以开始的时候每个 ...
- MAYA安装失败怎样卸载重新安装MAYA,解决MAYA安装失败的方法总结
技术帖:MAYA没有按照正确方式卸载,导致MAYA安装失败.楼主也查过网上关于如何解决MAYA安装失败的一些文章,是说删除几个MAYA文件和MAYA软件注册表就可以解决MAYA安装失败的问题,实际的情 ...
- Android GDI 图形渲染
发布于2011-07-26 导读:对于Android开发者来说,成系列的技术文章对他们的技术成长帮助最大.如下是我们向您强烈推荐的主题为Android开发的第一个系列文章. <Andro ...
- deepin 15.11 升级docker-ce 18.01到19.03.1,升级docker compose 1.23到1.24.1
1.升级docker compose ,docker官方安装方法 $ sudo curl -L "https://github.com/docker/compose/releases/dow ...
- 现代IM系统中的消息系统架构 - 模型篇
前言 在架构篇中我们介绍了现代IM消息系统的架构,介绍了Timeline的抽象模型以及基于Timeline模型构建的一个支持『消息漫游』.『多端同步』和『消息检索』多种高级功能的消息系统的典型架构.架 ...
- Activity学习(一):生命周期
一. 认识Activity Activity是Android的四大组件之一,那么它是什么呢?如果简单的理解,可以把它当成应用的一个显示的屏幕. Activity类处于android.app包中,继承体 ...
- PHP 面试题三
1.nginx使用哪种网络协议? nginx是应用层 我觉得从下往上的话 传输层用的是tcp/ip 应用层用的是http fastcgi负责调度进程 2. <? echo 'hello tush ...
- (二)C#编程基础复习——变量和常量
今天要复习一下C#基础中的变量和常量,所谓变量,就是用来存储特定类型的数据,分为值类型和引类型,可以根据需要随时改变变量中所村存储的数据值,变量必须先声明,然后才能赋值:常量就是固定不变的值,常量的变 ...
- 2019-8-2-WPF-从文件加载字体
title author date CreateTime categories WPF 从文件加载字体 lindexi 2019-08-02 17:10:33 +0800 2018-2-13 17:2 ...
- H3C 出站包过滤工作流程