二分查找
二分查找其实是一个搜索算法,而且在704 中考察的是有序的无重复元素的数组。二分就是利用数组的有序来减少复杂度,相比完全遍历更加快。
二分查找的关键在于确定搜索的区间。在每轮循环中,我们如何更改区间,缩小查找的范围。
对于常见的二分,有两种写法,一种是区间左闭右开,另一种是左闭右闭。其实两者是一样的,主要看你搜索的区间的定义。
左闭右开
首先是左闭右开。我们搜索的区间是从[left, right) 并不包括right, 也就是说明在每次改变区间时候,right是取不到的。因此,while 循环中是left<right 这样区间才合理比如[0, 1) [1, 2),如果left = right,此时遍历的区间为[0, 0) 这是一个不合理的区间。第二个关键点,right初始化和right的变化。right 应该等于middle-1还是right=middle。还是明确,我们搜索的区间是[left, right) right边界我们是不会搜索到的,当nums[mid] > target,搜索区间应该变为[left, mid) 因为mid此时已经大于 target,mid就不应该在下一轮搜索的区间内, 但是middle-1在。因此right应该等于mid,此时搜索区间在[left, mid) 包含mid-1但是不包含mid.
def binary_search(nums: List[int], target: int):
left, right = 0, len(nums) # right是取不到的
while left < right:
mid = (left + right) // 2
if (nums[mid]> target):
right = mid
elif (nums[mid] < target):
left = mid + 1
else:
return mid
return -1
左闭右闭
同样的,我们搜索的区间是从[left, right] 包括right, 也就是说明在每次改变区间时候,right是能够取到的。因此,while 循环中是left<=right 这样区间才合理比如[0, 1] [1, 2],如果left = right,此时遍历的区间为[0, 0]这仍然是一个合理的区间。第二个关键点,right初始化和right的变化。right 应该等于middle-1还是right=middle。还是明确,我们搜索的区间是[left, right] right边界我们是会搜索到的,当nums[mid] > target,搜索区间应该变为[left, mid] 因为mid此时已经大于 target,mid就不应该在下一轮搜索的区间内, 但是middle-1在。因此right应该等于mid-1,此时搜索区间在[left, right] 包含mid-1但是不包含mid.
def binary_search(nums: List[int], target: int):
# 左闭右闭
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right)//2
if (nums[mid]) > target:
right = mid - 1
elif (nums[mid] < target:
left = mid + 1
else:
return mid
return -1
移除元素
双向指针
移除这个题的解决思路可能一开始想不到用快慢指针,可能会想到用双指针,一个在最左边,一个在最右边(这是我做了二分后自然而然的一个思路),left 和right都在移动,直到相遇后退出循环。
那么这样做的话,就会有如下几种情况
1. left == val and right != val 那么可以直接赋值(或者两两交换) left +1 right - 1
2. left != val and right == val 那么 left+1 right-1
3. left !=val and right != val 那么left+1 right不动
4. left == val and right == val 那么 right -1 left 不动
循环条件要是[left, right] 因此可以取等
def removeElement(self, nums: List[int], val: int) -> int:
left, right = 0, len(nums)-1
while left <= right:
if nums[left] == val and nums[right] != val:
nums[left] = nums[right]
left += 1
right -= 1
elif nums[left] == val and nums[right] == val:
right -= 1
elif nums[left] != val and nums[right] != val:
left += 1
else:
left += 1
right -= 1
return left
快慢指针
这里是参考的代码随想录的解析。两个指针都是从0开始,一个slow,一个fast,fast去遍历整个数组,slow用来储存非val的值。那么,我们想知道快和慢该如何移动呢?
首先我们确定循环的终止条件,那就是fast将数组遍历结束了,那就是fast < len(nums),
第二个,如何移动这个指正,快和慢的理解。快是指的是,fast一直在移动,那么slow什么时候移动? 当nums[fast] != val的,我开始移动,将nums[slow]=nums[fast],我直接赋值,保存nums[fast]访问到的不等于val的值。
def removeElement(nums: List[int], val:int):
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
暴力法
暴力法其实就是思路就是 如果找到val 那么对应的所有元素向前移动一位.
在nums[left] == val的时候,left-1的目的是移动到前面的值也有可能是val,如果不减去,后面又回+1直接跳过去
def removeElement(nums:List[int], val: int):
left, right = 0, len(nums)
while left < right:
if nums[left] == val:
for j in range(left+1, len(nums):
nums[j-1] = nums[j]
right -= 1
left -= 1
left += 1

7469

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



