704. 二分查找
1. 文章链接
文档讲解:代码随想录 (programmercarl.com)
2. 看到题目的第一想法
有序数组->二分法
左边界left, 右边界right, 找到中间位置的元素的idx,
如果nums[idx]<target,说明target在nums的[idx + 1, right]的范围内。
如果nums[idx]>target,说明target在nums的[left, idx - 1]的范围内。
如果nums[idx]==target,说明target已经找到,返回idx。
但是什么情况下target算是没找到,返回-1呢?
答:应该是左右边界left和right已经相等了,这种情况下可能边界上也存在我们要的target, 因为明确判断不是target的idx已经被我们通过+1,-1的方式排除在范围之外了。
所以只有当left和right位置互换的情况下,即left > right的情况下才能下定论就是找不到了。
而且二分查找并不是每个区域都要探索,而是一半一半地缩小范围,所以实际上是的时间复杂度。
所以也不会太耗时。
class Solution:
def binary_search(self, nums: List[int], left_int: int, right_int: int, target: int) -> int:
idx_int = (left_int + right_int) >> 1
if nums[idx_int] < target:
# target在右侧。
if idx_int == right_int:
# 说明没找到
return -1
loc_int = self.binary_search(
nums=nums,
left_int=idx_int + 1,
right_int=right_int,
target=target
)
elif nums[idx_int] > target:
# target在左侧。
if left_int == idx_int:
return -1
loc_int = self.binary_search(
nums=nums,
left_int=left_int,
right_int=idx_int - 1,
target=target
)
else:
loc_int = idx_int
return loc_int
def search(self, nums: List[int], target: int) -> int:
left_int = 0
right_int = len(nums) - 1 # 右边界也包含在搜索范围内。
return self.binary_search(nums, left_int, right_int, target)
3. 看完代码随想录之后的想法
二分法不仅仅需要数组是有序数组,而且还要求数组中没有重复元素。
我保持了左闭右闭的循环不变量,但我没有用循环,而是用了递归。
下面这种找中位点的做法更好,可以防止溢出:
middle = left + (right - left) // 2
于是我按照循环又写了一遍:
class Solution:
def search(self, nums: List[int], target: int) -> int:
left_int = 0
right_int = len(nums) - 1 # 右边界也包含在搜索范围内。
while left_int <= right_int:
idx_int = left_int + (right_int - left_int) // 2
if nums[idx_int] < target:
# 在右侧
left_int = idx_int + 1
elif nums[idx_int] > target:
# 在左侧
right_int = idx_int - 1
else:
return idx_int
return -1
4. 实现过程中遇到哪些困难
如果用递归,不能使用直接截取num的一段传给下一层递归的方法,因为我们需要记录真实的坐标,所以只能传完整的nums.
5. 学习时长
构思时长:50分钟,因为中间有各种事情打扰。实际应该有30分钟吧。
实现+修改:30分钟。
27. 移除元素
1. 文章链接
文档讲解:代码随想录 (programmercarl.com)
2. 看到题目的第一想法
nums数组长度不超过100,我觉得暴力求解也可以。
最坏的情况就是每次都移动,每次移动n-i个元素。
那也大概是的时间。
看了力扣上的提示,我发现可以每次从最后一个位置拿一个元素来覆盖指定移除的元素。
用于覆盖的元素原来的位置的tail--,这样tail每次都指向最后一个有效元素。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
tail = len(nums) - 1
val_idx = 0
while val_idx <= tail:
if nums[val_idx] == val:
nums[val_idx] = nums[tail]
tail -= 1
else:
val_idx += 1
return tail + 1
3. 看完代码随想录之后的想法
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
其实我的实现方法也是一种双指针的思想,但是感觉不按套路出牌。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
slow_idx = 0
for fast_idx in range(len(nums)):
if nums[fast_idx] != val:
nums[slow_idx] = nums[fast_idx]
slow_idx += 1
# 如果fast_idx指向了val,不应该更新到nums[slow_idx]上,所以直接跳过看下一个。
return slow_idx
4. 实现过程中遇到哪些困难
当我打算用尾部元素替换前面的nums[idx]==val的元素时,我发现存在一个疏忽,那就是尾部元素有可能也是nums[tail]==val的,这样就白替换了,我想到的补救方法是,被覆盖的位置的idx不要动,等待下一次判断,如果还判断nums[idx]==val时,则还能那下一个新的tail去替换。
5. 学习时长
构思+实现:<30分钟