Day01 代码随想录刷题
知识点:二分查找、双指针
一、LeetCode704.二分查找
1.解题思路
二分查找首先要明确区间,根据区间不同有两种常用方法,第一种方法是左闭右闭[]
,第二种方法是左闭右开[)
。
2.代码实现
2.1 方法一:左闭右闭
因为左闭右闭,两侧的数据可能有效,当计算mid
后,若mid
所指的nums[mid]
不是目标值target
,则代表mid
的数据nums[mid]
无效,不能出现在两侧,所以需要对其进行加一或者减一的操作
返回值存在三种情况:
不存在
target
,R = L - 1,LR会出现反向超越情况存在
target
,在中间存在
target
,在两侧,R = L
# way 1 []
def lower_bound(nums, target):
l, r = 0, len(nums) - 1
while l <= r:
mid = l + (r - l)//2
if nums[mid] > target:
r = mid - 1
elif nums[mid] < target:
l = mid + 1
else:
return mid
return -1
class Solution:
def search(self, nums: List[int], target: int) -> int:
return lower_bound(nums, target)
2.2 方法二:左闭右开
因为左闭右开,右侧的数据无效,左侧的数据可能有效,当计算mid
后,若mid
所指的nums[mid]
不是目标值target
,则代表mid
的数据nums[mid]
无效,故不能出现在左侧但可以出现在右侧,所以只有在左侧更新时需要加一,右侧更新不需要减一的操作。
返回值存在三种情况:
不存在
target
,R = L,LR会出现相等情况存在
target
,在中间存在
target
,在左侧,R = L + 1
# way 2 [)
def lower_bound2(nums, target):
l,r = 0,len(nums)
while l < r:
mid = l + (r - l)//2
if nums[mid] > target:
r = mid
elif nums[mid] < target:
l = mid + 1
else:
return mid
return -1
class Solution:
def search(self, nums: List[int], target: int) -> int:
return lower_bound2(nums, target)
二、LeetCode027.移除元素
1.解题思路
使用快慢指针,快指针j
用来遍历数组的每个元素,慢指针i
作为下标来更新数组。
2.代码实现
关键在于当快指针
j
指向的值为要保留的数(!=val
)时,将其值更新到慢指针作为下标的数组中nums[i]
。数组元素无法被删除,只能被覆盖
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
i = 0
j = 0
# # while循环法
# while j < len(nums):
# if nums[j] != val:
# nums[i] = nums[j]
# i += 1
# j += 1
# return i
# for循环法
for j in range(0,len(nums)):
if nums[j] != val:
nums[i] = nums[j]
i+= 1
j += 1
return i
三、LeetCode034.在排序数组中查找元素的第一个和最后一个位置
1.解题思路
要找target
在nums
数组中的左右边界,无非存在 3 种情况:
target
在nums
列表中存在target
;
target
在nums
列表中不存在target
;
target < nums[0]
或者target > nums[n-1]
。
后2种情况可以通过以下代码排除:
if len(nums) == 0 or nums[0] > target or nums[-1] < target:
return [-1,-1]
只剩第一种情况,我们需要利用两次二分法来确定左右边界,确定边界的二分法与普通二分法不同,如确定左边界:
当nums[mid] == target
时,程序不返回mid
而是继续将区间向左压缩,所以是right = mid - 1
;
相反,确定右边界时,当nums[mid] == target
时,执行right = mid + 1
。
这么做的原因是当找到target
时不一定是最左或者最右的target
。
2.代码实现
class Solution:
# 二分法
def leftMargin(self,nums,target):
l, r = 0, len(nums) - 1
while l <= r:
mid = l + (r - l)//2
if nums[mid] < target:
l = mid + 1
else:
r = mid - 1
if nums[l] == target:
return l
else:
return -1
def rightMargin(self,nums,target):
l, r = 0, len(nums) - 1
while l <= r:
mid = l + (r - l)//2
if nums[mid] <= target:
l = mid + 1
else:
r = mid - 1
if nums[r] == target:
return r
else:
return -1
def searchRange(self, nums: List[int], target: int) -> List[int]:
# # 暴力解法
# l = 0
# r = len(nums) - 1
# while l < r and (nums[l]!=target or nums[r]!=target):
# if nums[l] != target:
# l += 1
# if nums[r] != target:
# r -= 1
# if l > r:
# return [-1,-1]
# elif l == r:
# if nums[l] == target:
# return [l,r]
# else:
# return [-1,-1]
# return [l,r]
# 二分法
if len(nums) == 0 or nums[0] > target or nums[-1] < target:
return [-1,-1]
return[self.leftMargin(nums,target),self.rightMargin(nums,target)]
四、LeetCode035.搜索插入位置
1.解题思路
还是二分法,while 循环的条件是left <= right
,
1.1 当数组中有值为target
的元素时,按照普通的二分法,找到该值的索引后返回即可;
1.2 当数组中没有值为target
的元素时,最后一次循环搜寻区间可能为一个或两个元素,即right = left
或right = left + 1
,这两种情况时都有mid = left
,
如果最后一次循环时
mid
所在位置的值大,即nums[mid] > target
,则right = mid - 1
结束循环,不影响left
,且left
的位置就是要插入的位置;如果最后一次循环时
mid
所在位置的值小,即nums[mid] < target
,则left = mid + 1
结束循环,此时更新后的left
的位置就是要插入的位置;综上,若数组内没有值为
target
的元素,最后都只需要返回left
的索引。
2.代码实现
只需要将普通二分法最后的
return -1
改为return left
即可
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
# # 暴力解法
# i = 0
# j = 0
# for j in range(0,len(nums)):
# if nums[j] >= target:
# return j
# j += 1
# return j
# 二分法
l = 0
r = len(nums) - 1
while l <= r:
mid = l + (r -l)//2
if nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid - 1
else:
return mid
return l