目录
leetcode - 704
二分查找有两种范式,一种是左闭右开,一种是左闭右闭。
如图,左边指的是左闭右开,初始状态下,start = 0,end = len(nums),也就是最后一个元素后一位,右侧对应的是一个开区间。左闭右开的情况下,start 是不会等于end的,所以循环条件就是start < end , 当target < nums[mid]时,end = mid,这里记住开区间start = mid就行,任何时候target > nums[mid]时,start = mid +1
右边指的是左闭右闭,start = 0 ,end = len(nums) -1也就是对应我们最后一个元素,右侧对应的是一个闭区间。闭区间,srart是可以等于end的,所以循环条件是 start <= end。 当target < nums[mid]时,start = mid-1。
时间复杂度为(logn), 空间复杂度为1, 若用递归的方式,时间复杂度为(logn),空间复杂度为递归深度乘以每次递归的空间复杂度 即: logn * 1 = logn
# 左闭右闭
def search(nums: List[int], target: int) -> int:
start = 0
end = len(nums)-1
while start <= end:
mid = (start + end) //2
if nums[mid] == target:
return mid
elif nums[mid] > target:
end = mid-1
else:
start = mid+1
else:
return -1
# 左闭右开
def search(nums: List[int], target: int) -> int:
start = 0
end = len(nums)
while start < end:
mid = (start + end) //2
if nums[mid] == target:
return mid
elif nums[mid] > target:
end = mid
else:
start = mid+1
else:
return -1
衍生 - 35
关于插入问题
第一种方法是同上面的方法
def searchInsert(nums: List[int], target: int) -> int:
start = 0
end = len(nums) -1
while start <= end:
mid = (start + end) //2
if nums[mid] == target:
return mid
elif nums[mid] > target:
end = mid -1
else:
start = mid+1
else:
return start
先查找这个值,如果找不到就插在start的位置上
但是更为常用的是 循环条件为 start < end ,对于start 和 end的操作分别是 start = mid+1 ,end = mid 这种方式在二分法中(非单纯二分查找)更为常用
def searchInsert(nums: List[int], target: int) -> int:
start = 0
end = len(nums) -1
while start < end:
mid = (start + end) //2
if nums[mid] == target:
return mid
elif nums[mid] > target:
end = mid
else:
start = mid+1
else:
# 如果target 大于start处的元素,那么就插在start的后一位
if nums[start] < target:
return start +1
return start
衍生 - 34
首先是在第一题的二分大框架下,找到目标元素后,向其左右遍历寻找即可,题目没啥新意
def searchRange(nums: List[int], target: int) -> List[int]:
start = 0
end = len(nums) -1
while start <= end:
mid = (start + end) //2
if nums[mid] == target:
res = [mid]
# 从mid 出发 寻找前后相邻目标元素的小标,一旦遍历到非目标元素 停止遍历
for k in range(mid+1,len(nums)):
if nums[k] == target:
res.append(k)
else:
break
# 从 mid出发,到0结束,因为是反向遍历,所以步长是-1
for k in range(mid,-1,-1):
if nums[k] == target:
res.append(k)
else:
break
return [min(res),max(res)]
elif nums[mid] > target:
end = mid -1
else:
start = mid+1
else:
return [-1,-1]
Leetcode - 27
首先明确 这道题是让你返回新数组的长度,假设新数组长度为s,则题目会默认截取原数组中前s个元素与答案对比
这题最简单的方法就是 边删除目标元素 边判断数组中是否还存在目标函数,如果不存在则停止循环
def removeElement(nums: List[int], val: int) -> int:
while val in nums:
nums.remove(val)
return len(nums)
另一种是用快慢指针的方法,快指针用于不断向后遍历新元素,慢指针用于表示更新后数组的最新状态,其实就是一个将非目标元素不断往数组前面填充的过程,慢指针从下标0处开始,快指针一旦找到非目标元素,就扔给慢指针处,慢指针处记录这个值后,慢指针再指向下一个下标,思想很好
def removeElement(nums: List[int], val: int) -> int:
# 快指针遍历元素
fast = 0
# 慢指针记录位置
slow = 0
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow