文章目录
二分法
旋转排序数组
寻找旋转排序数组中的最小值
寻找旋转排序数组中的最小值
只需要考虑mid与右边界的情况
class Solution:
def findMin(self, arr: List[int]) -> int:
n = len(arr)
l,r = 0,n-1
# 3 1 2
while l<=r:
mid=l+(r-l)//2
if arr[mid] < arr[r]:
r = mid
else:
l = mid+1
return arr[r]
33. 搜索旋转排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
虽然相比较单纯的最大值、最小值查找会麻烦一些(考虑target),但不包含重复数字,问题简化了很多。
每次二分,根据nums[mid]与nums[r]比较得出当前的场景。
class Solution:
def search(self, nums: List[int], target: int) -> int:
l,r=0,len(nums)-1
while l <= r:
mid = l + (r-l)//2
if nums[mid]== target:
return mid
if nums[mid] > nums[r]:
if target <= nums[r] or target > nums[mid]:
l = mid+1
else:
r = mid-1
else:
if target >nums[mid] and target <= nums[r]:
l = mid+1
else:
r = mid-1
return -1
寻找旋转排序数组中的target,如果有多个,返回最小索引的那个
面试题 10.03. 搜索旋转数组
需要考虑mid、右边界、target的关系。此外,可能包含重复数字,问题更复杂一些
1.先区分递增递减关系。
2.根据递增递减关系找到二分方向。
class Solution:
def search(self, arr: List[int], target: int) -> int:
n = len(arr)
if n==0: return -1
if arr[0]==target: return 0
l,r = 0,n-1
# [3,1,2]
while l<=r:
mid=l+(r-l)//2
# print(mid, arr[mid])
if arr[mid]==target:
while mid>0 and arr[mid-1]==arr[mid]:
mid -= 1
return mid
# mid~r 先递增后递减
if arr[mid] > arr[r]:
if arr[mid] <target or target <= arr[r]:
l = mid+1
else:
r = mid-1
# mid~r 递增
elif arr[mid] < arr[r]:
if arr[mid] > target or target > arr[r]:
r = mid-1
else:
l = mid+1
else:
r -= 1
# print(mid, l,r)
return -1
查找target
34. 在排序数组中查找元素的第一个和最后一个位置
34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
if n==0: return [-1,-1]
# 先找最大
l,r = 0,n-1
while l<=r:
mid = l+(r-l)//2
if nums[mid]<=target:
l = mid+1
else:
r = mid-1
# print(l,r)
if nums[r]!=target:
return [-1,-1]
ans = [r,r]
l=0
while l<=r:
mid = l+(r-l)//2
if nums[mid]>=target:
r = mid-1
else:
l = mid+1
ans[0] = l
return ans
面试题 10.05. 稀疏数组搜索
稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。
class Solution:
def findString(self, words: List[str], s: str) -> int:
n = len(words)
l,r = 0,n-1
# 过滤混淆值,看目标字符串是否位于区间内
while l<n and words[l]=='':
l += 1
if l==n: return -1
while r>=0 and words[r]=='':
r -= 1
if self.lower(s,words[l]) or self.lower(words[r], s):
return -1
# 二分查找
while l<=r:
mid = l+(r-l)//2
end = mid
# 过滤混淆值
while end>=l and words[end]=='':
end -= 1
if end <l:
l = mid+1
continue
# 根据与目标值的比较选择方向及起始点
if words[end]==s:
return end
elif self.lower(s,words[end]):
r = end-1
else:
l = mid+1
return -1
def lower(self, s1, s2):
# s1<s2 返回True
m,n=len(s1),len(s2)
for i in range(min(m,n)):
if s1[i]<s2[i]:
return True
elif s1[i]>s2[i]:
return False
return m<n
魔术索引
面试题 08.03.魔术索引
面试题 08.03.魔术索引
魔术索引。 在数组A[0…n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。
class Solution:
def findMagicIndex(self, nums: List[int]) -> int:
n = len(nums)
def get(l,r):
if l>r: return -1
mid = l+(r-l)//2
ans = get(l,mid-1)
if ans > -1:
return ans
elif nums[mid]==mid:
return mid
else:
return get(mid+1,r)
left,right=0,n-1
return get(left, right)
查找峰值
162. 寻找峰值
162. 寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
n = len(nums)
l,r = 0,n-1
while l<r:
mid = l+(r-l)//2
if (mid == 0 or nums[mid-1]<nums[mid]) and (nums[mid] > nums[mid+1]):
return mid
if mid>0 and nums[mid] <=nums[mid-1]:
r = mid-1
elif nums[mid]<=nums[mid+1]:
l = mid+1
return r
位运算
222. 完全二叉树的节点个数
222. 完全二叉树的节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
if not root: return 0
t = root
h = 0
while t:
h += 1
t = t.left
l, r = 0, (1<<(h-1))-1
while l <= r:
mid = l+(r-l)//2
t = root
for i in range(h-2,-1,-1):
if mid & (1<<i):
t = t.right
else:
t = t.left
if t:
ans = t
l = mid+1
else:
r = mid-1
# print(l,r)
ans = (1<<(h-1))-1+r+1
return ans