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
文章详细介绍了二分查找的两种方法,包括左闭右闭和左闭右开的情况,并提供了对应的Python代码实现。同时,讲解了使用双指针解决LeetCode027题的移除元素问题。此外,还讨论了在排序数组中查找元素范围的LeetCode034题,通过二分法寻找目标元素的边界。最后,介绍了搜索插入位置的LeetCode035题,同样采用二分查找策略。
2103

被折叠的 条评论
为什么被折叠?



