日常系列LeetCode《8·二分查找篇》
Posted 常某某的好奇心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列LeetCode《8·二分查找篇》相关的知识,希望对你有一定的参考价值。
数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)
总结
1.二分查找的基础知识
基本的二分查找-(规则:利用数组的有序性,查中间的元素)
普通二分查找的复杂度:o(logn),o(1)
迭代二分查找的复杂度:o(logn),o(logn)
#适用:有序数组(这里以升序列表为例)
#o(logn),o(1)
#方法1:不断找中间值
def contains(nums,target):
if nums is None or len(nums)==0:
return False
left,right=0,len(nums)-1
while left<=right:
#溢出bug:mid=(left+right)/2(因为int最大值2^31-1=2147483647)
mid=left+(right-right)//2
if target==nums[mid]:
return True
elif target<nums[mid]:
right=mid-1
else:
right=mid+1
return False
#方法二:递归写法
#o(logn),o(logn)
def containsR(nums,target):
if nums is None or len(nums)==0:
return False
return contains_help(nums,0,len(nums)-1,target)
def contains_help(nums,left,right,target):
#终止条件
if left>right:
return False
#公式
mid=left+(right-left)//2
if nums[mid]==target:
return True
elif target<nums[mid]:
return contains_help(nums,left,right-1,target)
else:
return contains_help(nums,left-1,right,target)
2.变形二分查找算法及其应用
#返回第一个等于Target的index
def firstTargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)-1
while left<=right:
mid=left+(right-left)//2
if target==nums[mid]:
if mid==0 or nums[mid-1]!=target:
return mid
else:
right=mid-1
elif target>nums[mid]:
left=mid+1
else:
right=mid-1
return -1
#返回第一个大于或等于Target的index
def firstGEtargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]>=target:
if mid==0 or nums[mid-1]<target:
return mid
else:
right=mid-1
else:
left=mid+1
return -1
#返回最后一个等于Target的index
def lastTargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]==target:
if mid==len(nums)-1 or nums[mid+1]!=target:
return mid
else:
left=mid+1
elif nums[mid]>target:
right=mid-1
else:
left=mid+1
return -1
# 返回最后一个小于或等于Target的index
def lastLETargetIndex(nums,target):
if nums is None or len(nums)==0:
return
left,right=0,len(nums)
while left<=right:
mid=left+(right-left)//2
if nums[mid]<=target:
if mid==len(nums)-1 or nums[mid+1]>target:
return mid
else:
left=mid+1
else:
right=mid-1
#left>tight没有元素
return -1
3.山脉数组、旋转数组
4.有的时候,需要对题目抽象,才能找出二分的性质
lc 704:二分查找
https://leetcode.cn/problems/binary-search/
提示:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。
#思路一:不断在循环体中查找目标元素
class Solution:
def search(self, nums: List[int], target: int) -> int:
#
n=len(nums)
if n==0:return -1
#o(logn),o(1)
left=0
right=n-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]==target:
return mid
elif nums[mid]>target:
right-=1
else:left=mid+1
#
return -1
#思路二:在循环体中排除一定不存在目标元素的区间
class Solution:
def search(self, nums: List[int], target: int) -> int:
#
n=len(nums)
if n==0:return -1
#o(logn),o(1)
left=0
right=n-1
while left<right:
mid=left+(right-left+1)//2#防止死循环(left=mid)
if nums[mid]>target:
right=mid-1
else:left=mid
#循环后处理
if nums[left]==target:return left
#
return -1
lc 34【top100】:排序数组中找元素的第一个和最后一个位置
https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
提示:
0 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
nums 是一个非递减数组
-10^9 <= target <= 10^9
#思路一:
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
#o(1)
if len(nums) == 0: return [-1, -1]
#o(logn)
id1=self.firstsearch(nums,target)
id2=self.secondsearch(nums,target)
#
if id1==-1 or id2==-1:return [-1,-1]
else:return [id1,id2]
def firstsearch(self,nums,target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right-left) // 2
if nums[mid] == target:
if mid == 0 or nums[mid - 1] != target: return mid
else: right = mid - 1
elif nums[mid] < target: left = mid + 1
else: right = mid - 1
return -1
def secondsearch(self,nums,target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right-left) // 2
if nums[mid] == target:
if mid == len(nums) - 1 or nums[mid + 1] != target: return mid
else: left = mid + 1
elif nums[mid] < target: left = mid + 1
else: right = mid - 1
#
return -1
#思路二(超时+不推荐):
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
#o(1)
if len(nums) == 0: return [-1, -1]
#o(logn)
id1=self.firstsearch(nums,target)
id2=self.secondsearch(nums,target)
#
if id1==-1 or id2==-1:return [-1,-1]
else:return [id1,id2]
def firstsearch(self,nums,target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right-left) // 2
if nums[mid] < target: #key:第一个
left=mid+1
else: right = mid
if nums[left]==target:return left
else:return -1
def secondsearch(self,nums,target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right-left+1) // 2
if nums[mid] > target: #key:最后一个
right=mid-1
else: left = mid
if nums[left]==target:return left
else:return -1
lc 35 【剑指 53-1】【top100】:搜索插入位置
https://leetcode.cn/problems/search-insert-position/
提示:
1 <= nums.length <= 10^4
-104 <= nums[i] <= 10^4
nums 为 无重复元素 的 升序 排列数组
-10^4 <= target <= 10^4
#本质:找到第一个大于等于target 的元素的下标
#思路一
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
#
n=len(nums)
if n==0:return 0
if target>nums[n-1]:return n
#o(logn),o(1)
left=0
right=n-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]>=target:
if mid==0 or nums[mid-1]<target:return mid
else:right=mid-1
else:left=mid+1
return -1
#思路二:优化
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
#
n=len(nums)
if n==0:return 0
#if target>nums[n-1]:return n
#o(logn),o(1)
left=0
right=n#n-1
while left<right:
mid=left+(right-left)//2
if nums[mid]<target: #‘第一等于target’
left=mid+1
else:right=mid
return left
lc 278:第一个错误的版本
https://leetcode.cn/problems/first-bad-version/
提示:
1 <= bad <= n <= 2^31 - 1
#key:在一个升序的数组中,找到第一个等于n的位置
#思路一:
# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:
class Solution:
def firstBadVersion(self, n: int) -> int:
#o(logn),o(1)
left=1
right=n
while left<=right:
mid=left+(right-left)//2
if isBadVersion(mid):
if mid==1 or isBadVersion(mid-1)==False:return mid
else:right=mid-1
else:left=mid+1
return -1
#思路二:
# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:
class Solution:
def firstBadVersion(self, n: int) -> int:
#o(logn),o(1)
left=1
right=n
while left<right:
mid=left+(right-left)//2
if isBadVersion(mid):
right=mid #key
else:left=mid+1
return left if isBadVersion(mid) else -1 #key
lc 33【top100】:搜索旋转排序数组
https://leetcode.cn/problems/search-in-rotated-sorted-array/
提示:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10^4 <= target <= 10^4
'''
key:部分有序性
nums[1eft]<= nums[mid]:[left,mid]有序
nums[1eft]> nums[mid]:[mid,right]有序
'''
class Solution:
def search(self, nums: List[int], target: int) -> int:
#o(n),o(1)
n=len(nums)
if n==0:return -1
#
left=0
right=n-1
while left<=right:
mid=left+(right-left)//2
#
if nums[mid]==target:return mid
#左有序
if nums[left]<=nums[mid]:
if nums[left]<=target<nums[mid]:
right=mid-1
else:left=mid+1
#右有序
else:
if nums[mid]<target<=nums[right]:
left=mid+1
else:right=mid-1
#
return -1
lc 153:寻找旋转排序数组中的最小值
https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums 中的所有整数 互不相同
nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
#暴力
class Solution:
def findMin(self, nums: List[int]) -> int:
#o(n),o(1)
n=len(nums)
for i in range(1,n):
if nums[i]<nums[i-1]:
return nums[i]
return nums[0]
#二分
class Solution:
def findMin(self, nums: List[int]) -> int:
#o(n),o(1)
if len(nums)==0:return 0
left=0
right=len(nums)-1
#
while left<right:
mid=left以上是关于日常系列LeetCode《8·二分查找篇》的主要内容,如果未能解决你的问题,请参考以下文章