目录
2020/9/29
69 x 的平方根
class Solution:
def mySqrt(self, x: int) -> int:
if(x<2):
return x
l=0
h=x
while(l<=h):
mid=int((l+h)/2)
#print(mid)
if(mid*mid==x or mid==l):
return mid
if mid*mid<x:
l=mid
else:
h=mid
2020/10/2
744. 寻找比目标字母大的最小字母
- 记得low+1 不然可能一直循环
- 找不到就返回第一个元素
class Solution:
def nextGreatestLetter(self, letters: List[str], target: str) -> str:
l=0
h=len(letters)-1
while(l<h):
mid=int((l+h)/2)
if(letters[mid]>target):
h=mid
else:
l=mid+1 #不+1可能一直循环
#如果最后一个元素都小于= target 就返回首元素
return letters[l] if letters[l]>target else letters[0]
540. 有序数组中的单一元素
注意一直确定mid为偶数
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
l=0
h=len(nums)-1
if(h==0):
return nums[0]
while(l<h):
mid=int((l+h)/2)
mid=mid-1 if mid%2==1 else mid
if(nums[mid]==nums[mid+1]):
l=mid+2
else:
h=mid
return nums[l]
278. 第一个错误的版本
# The isBadVersion API is already defined for you.
# @param version, an integer
# @return a bool
# def isBadVersion(version):
class Solution:
def firstBadVersion(self, n):
"""
:type n: int
:rtype: int
"""
l=1
h=n
while(l<h):
mid=int((l+h)/2)
if(isBadVersion(mid)):
h=mid
else:
l=mid+1
return l
153. 寻找旋转排序数组中的最小值
大多时候都要 l=mid+1 不然容易死循环
class Solution:
def findMin(self, nums: List[int]) -> int:
l=0
h=len(nums)-1
while(l<h):
mid=int((l+h)/2)
if(nums[mid]>nums[h]):
l=mid+1
else:
h=mid
return nums[l]
34. 在排序数组中查找元素的第一个和最后一个位置
注意空数组
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if(not nums):
return [-1,-1]
l=0
h=len(nums)-1
start=-1
end=-1
while(l<h):
mid=int((l+h)/2)
if(nums[mid]<target):
l=mid+1
else:
h=mid
index=l
if(nums[index]==target):
start,end=index,index
while(start-1>=0 and nums[start-1]==nums[start]):
start-=1
while(end+1<len(nums) and nums[end+1]==nums[end]):
end+=1
return [start,end]
官方解法 两次二分查找 找到第一个大于等于target的index
class Solution:
#多少个数小于target 或者说第一个大于等于target的index
def extreme_insertion_index(self, nums, target):
lo = 0
hi = len(nums)#注意 因为返回值有可能是数组长度
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] >= target : #注意加等于
hi = mid
else:
lo = mid+1#注意+1
return lo
def searchRange(self, nums, target):
left_idx = self.extreme_insertion_index(nums, target)
#对应 target大于所有数 和 第一个大于等于target的数 不等于target
if left_idx == len(nums) or nums[left_idx] != target:
return [-1, -1]
return [left_idx, self.extreme_insertion_index(nums, target+1)-1]
解析二分查找 边界情况
https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/er-fen-cha-zhao-suan-fa-xi-jie-xiang-jie-by-labula/
2021/6/1
边界+1注意 一定要left+1 right-1避免死循环
注意边界条件,比如要找升序数组中第一个小于target的
注意l=mid还是l=mid+1
- l=mid+1,最后的nums[h]是小于等于target的,因为nums[mid]虽然<target,但是l=mid+1,nums[mid+1]有可能=target
- l=mid,最后的nums[h]肯定小于target,或者极端情况下h=-1;h就是小于target的第一个位置;因为l=mid强制了Nums[l]是小于target的,但是这种情况下肯能会导致 l=h-1的情况下 死循环 无法终止
# 找第一个小于target的
def findLeft(self,nums,target):
l=0
h=len(nums)-1
# 从high往low找
while(l<h):
mid=(l+h)//2
if(nums[mid]>=target):
h=mid-1
else:
l=mid+1 # l=mid+1的情况下 nums[l]可能=target 所以h也可能等于target
# h此时是第一个小于target的 但是也有可能=target
return h
33. 搜索旋转排序数组
两部分有序数组,关键就是判断target在前半段还是后半段以及mid在前半段还是后半段
class Solution:
def search(self, nums: List[int], target: int) -> int:
if(len(nums)==0):
return -1
# nums[-1]>=target 说明在后半段
# nums[-1]<target 说明在前半段
right_flag=nums[-1]>=target # true target在后半段
left=0
right=len(nums)-1
while(left<=right):
mid=left+(right-left)//2
if(nums[mid]==target):
return mid
# mid在前半段
if(nums[mid]>nums[-1]):
if(right_flag):
left=mid+1
# target在前半段
else:
if(nums[mid]>target):
right=mid-1
else:
left=mid+1
# mid在后半段
else:
# target在前半段
if(not right_flag):
right=mid-1
else:
if(nums[mid]>target):
right=mid-1
else:
left=mid+1
return -1
class Solution:
def search(self, nums: List[int], target: int) -> int:
if(len(nums)==0):
return -1
# nums[-1]>=target 说明在后半段
# nums[-1]<target 说明在前半段
right_flag=nums[-1]>=target # true target在后半段
left=0
right=len(nums)-1
while(left<=right):
mid=left+(right-left)//2
if(nums[mid]==target):
return mid
# 1 mid target在同一段 正常搜索
if((right_flag and nums[mid]<=nums[-1]) or (not right_flag and nums[mid]>nums[-1])):
if(nums[mid]>target):
right=mid-1
else:
left=mid+1
# 2 mid target不在同一段 分情况
else:
# 2-1 target 后半段
if(right_flag):
left=mid+1
# 2-2 target 前半段
else:
right=mid-1
return -1
74. 搜索二维矩阵
- 1 先二分查找所在的行
- 2 再二分查找具体位置
也可以当作一个升序数组,直接对数组的Index映射成martix的坐标
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
if(not matrix or not matrix[0]):
return False
# 1 先二分查找所在的行
# 2 再二分查找具体位置
row=len(matrix)
col=len(matrix[0])
left=0
right=row-1
if(matrix[0][0]>target or matrix[row-1][col-1]<target):
return False
while(left<=right):
mid=left+(right-left)//2
if(matrix[mid][0]<=target and matrix[mid][col-1]>=target):
# # 考虑这一列都等于target时 left+1
# if(matrix[mid][0]==matrix[mid][col-1]):
# left+=1
left=mid
break
elif(matrix[mid][0]>target):
right=mid-1
else:
left=mid+1
# target在left这一row
cur_row=left
left=0
right=col-1
while(left<=right):
mid=left+(right-left)//2
if(matrix[cur_row][mid]==target):
return True
elif(matrix[cur_row][mid]>target):
right=mid-1
else:
left=mid+1
return False
81. 搜索旋转排序数组 II
- 关键是包含重复元素 ,重复元素可能即在左,又在右,当nums[left]=nums[mid]=nums[right]时无法区分到底下一步去前半部分还是后半部分(left+1 right-1 继续判断)
- 以及target也要随时判断在左还是右 (因为left right是可变的不是fix)
不过个人感觉不如将首元素和尾元素相等一直剔除 再用老方法fix left right,更加快
class Solution:
def search(self, nums: List[int], target: int) -> bool:
if(not nums):
return False
# 判断target和mid在不在同一段
left=0
right=len(nums)-1
while(left<=right):
mid=left+(right-left)//2
if(nums[mid]==target):
return True
# 此时无法确定mid在左边还是右边
if(nums[mid]==nums[left]==nums[right]):
left+=1
right-=1
continue
# mid在左边
if(nums[mid]>=nums[left]):
# 1 target 在左边
# 1-1 target<mid right=mid-1
# 1-2 target>mid left=mid+1
# 2 target 在右边 left=mid+1
if(nums[mid]>target and target>=nums[left]):
right=mid-1
else:
left=mid+1
# mid在右边
else:
# 1 target 在右边
# 1-1 target<mid right=mid-1
# 1-2 target>mid left=mid+1
# 2 target 在左边 right=mid-1
if(nums[mid]<target and target<=nums[right]):
left=mid+1
else:
right=mid-1
return False
2021/6/2
50. Pow(x, n)
递归空间复杂度 o(logn),还可以写成二进制思想的计算,空间复杂度降为o(1),但是不太能看懂思路
class Solution:
def myPow(self, x: float, n: int) -> float:
# 负号
# 奇数偶数
ret=1.0
if(n>=0):
return self.pow(x,n)
else:
return 1/self.pow(x,-n)
def pow(self,x,n):
if(n==1):
return x
if(n==0):
return 1
# n为偶数
tmp=self.pow(x,n//2)
if(n%2==0):
return tmp*tmp
else:
return tmp*tmp*x
162. 寻找峰值
这个题只要想明白 -inf和-inf之间一定有峰值,那么只要mid>mid+1,左边一定有峰值;mid<mid+1,右边一定有峰值
另外 迭代比递归省空间
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
left=0
right=len(nums)-1
# 所以只要出现 mid>mid+1 左边必定有峰值
while(left<=right):
if(left==right):
return left
mid=left+(right-left)//2
# 只要出现 mid>mid+1 左边必定有峰值
if(nums[mid]>nums[mid+1]):
right=mid
else:
left=mid+1
2021/6/3
4. 寻找两个正序数组的中位数
先写一个o(m+n)的思路
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
len1,len2=len(nums1),len(nums2)
mid=(len1+len2)//2
p1=0
p2=0
num1,num2=0,0
# 奇数走到第mid个数
# 偶数走到mid mid-1
for i in range(0,mid+1):
num1=num2
if(p1==len1):
num2=nums2[p2]
p2+=1
elif(p2==len2):
num2=nums1[p1]
p1+=1
else:
if(nums1[p1]<nums2[p2]):
num2=nums1[p1]
p1+=1
else:
num2=nums2[p2]
p2+=1
# print(num1)
# print(num2)
if((len1+len2)%2==0):
return (num1+num2)/2
else:
return num2
154. 寻找旋转排序数组中的最小值 II
class Solution:
def findMin(self, nums: List[int]) -> int:
if(nums[-1]>nums[0]):
return nums[0]
left=0
right=len(nums)-1
while(left<=right):
mid=(left+right)//2
if(nums[left]==nums[mid]==nums[right]):
left+=1
right-=1
continue
if(nums[right]>=nums[mid]):
right=mid
else:
left=mid+1
return nums[mid]
209. 长度最小的子数组
双指针
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if(nums[0]>=target):
return 1
if(len(nums)==1):
return 0
# 双指针
ret=0
p1=0
p2=1
cur_sum=nums[0]+nums[1]
# 直到p2滑动到末尾
while(p2<len(nums)):
# 超出最大值时 p1右走
if(cur_sum>=target):
ret=min(ret,p2-p1+1) if ret>0 else p2-p1+1
cur_sum-=nums[p1]
p1+=1
else:
p2+=1
if(p2==len(nums)):
break
cur_sum+=nums[p2]
return ret
官方给的双指针更加简洁
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if(not nums):
return 0
ret=0
p1=0
p2=0
cur_sum=0
# 直到p2滑动到末尾
while(p2<len(nums)):
cur_sum+=nums[p2]
# 1 cur_sum大于等于target
while(cur_sum>=target): # 不用考虑p1<=p2 因为相等时 cursum=0
ret=min(ret,p2-p1+1) if ret>0 else p2-p1+1
if(ret==1):
return 1
cur_sum-=nums[p1]
p1+=1
# 2 直到cur_sum<target
p2+=1
return ret