二分查找数组实现
非递归实现
def bsearch(nums,n,value):
low = 0
high = n-1
while low<=high:
mid = low+(high-low)/2
if nums[mid]==value:
return mid
elif nums[mid]<value:
low = mid+1
else:
high = mid-1
return -1
注意:
- **循环退出条件 low <= high **
- mid取值 (low+high)/2 可能导致溢出 可以改成
low+(high-low/2) or low+((high-low)>>1)
递归实现
def bsearch(nums,n,value):
def bsearchHelper(nums,low,high,value):
if low > high:
return -1
mid = low+(high-low)/2
if mid == value:
return mid
elif mid < value:
return bsearchHelper(nums,mid+1,high,value)
else:
return bsearchHelper(nums,low,mid-1,value)
局限性
- 依赖于顺序表结构,即数组(需要支持随机访问)
- 针对有序数据
先排序,因此插入删除操作过多的场景,不适合二分,适合二叉树。 - 数据量太小不适合
若数据之间的比较操作非常耗时,如300以上长度字符串比较,要可能减少比较次数,建议用二分 - 数据量过大不适合
依赖于数组,需要连续的内存空间
如何在1000万个整数中快速查找某个数
- 二分
- 假设最大的是2000w,那么设置一个字节数组Byte arr[2000w]
遍历每一个数字,x/8可得下标,x%8可得8位字节中的位数,存在置为1
查找时只需经过x/8,x%8取得相应的bit位,就可判断该数字是否存在。
二分查找,找重复数字
def bsearch(nums,n,value):
low = 0
high = n-1
while low <= high:
mid = int(low + (high-low)/2)
if nums[mid]>value:
high = mid-1
elif nums[mid]<value:
low = mid+1
else:
if mid == 0 or nums[mid-1]!=value:
return mid
else:
high = mid-1
return -1
leetcode34. 在排序数组中查找元素的第一个和最后一个位置
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
a = self.besearch(nums,target,0)
b = self.besearch(nums,target,1))
return [a,b]
def besearch(self,nums,target,tag):
low = 0
high = len(nums)-1
while low <= high:
mid = int(low+(high-low)/2)
if nums[mid] > target:
high = mid-1
elif nums[mid] < target:
low = mid+1
else:
if tag==0:
if mid == 0 or nums[mid-1] != target:
return mid
else:
high = mid-1
if tag==1:
if mid == len(nums)-1 or nums[mid+1] != target:
return mid
else:
low = mid+1
return -1
大佬做法:将返回值都设为low,因为返回值是low的原因,所以要增加一个if nums[a]!=target,不等则targer不存在,直接返回[-1,-1]
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
a = self.besearch(nums,target)
b = self.besearch(nums,target+1)
if a ==len(nums) or nums[a]!=target:
return [-1,-1]
return [a,b-1]
def besearch(self,nums,target):
low = 0
high = len(nums)-1
while low <= high:
mid = int(low+(high-low)/2)
if nums[mid] >= target:
high = mid-1
elif nums[mid] < target:
low = mid+1
else:
if mid == 0 or nums[mid-1] != target:
return mid
else:
high = mid-1
return low
leetcode 33. 搜索旋转排序数组
因为是旋转数组,只可能出现这种情况的排序
因此取mid后,首先与nums[0]比较,若比其大,则证明左段为顺序排列的,那么只需判断target是否在左段,就可决定high和low的值;
若比其小,则表示左段是有旋转段的,那么右段肯定是顺序段,则比较mid和len(nums)-1位置的大小。
class Solution:
def search(self, nums: List[int], target: int) -> int:
low = 0
high = len(nums)-1
while low <= high:
mid = low+(high-low)//2
if nums[mid]==target:
return mid
if nums[0] <= nums[mid]:
if nums[0]<=target<nums[mid]:
high = mid-1
else:
low = mid+1
else:
if nums[mid]<target<=nums[len(nums)-1]:
low = mid+1
else:
high = mid-1
return -1
658. 找到 K 个最接近的元素
二分查找链表实现—跳表
动态数据结构,可以快速支持插入,删除,查找O(logm)。
跳表—连标加多级索引的结构,也是redis的有序集合实现。
跳表的索引节点浪费内存?
在开发中,原始链表中存储的是很大的对象,但是索引节点只需存储关键词和指针,可忽略不计。
高效的动态插入和删除
插入数据:
删除数据:
需要获取前驱节点,删除原始链表结点和索引结点。
索引动态更新
通过随机函数,决定结点插入到哪几级索引中。
可按照区间查找数据,效率高于红黑树
同b+树??