LeetCode 704.二分查找
文档讲解:代码随想录
视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili
题目链接:. - 力扣(LeetCode)
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例 1:
输入:nums= [-1,0,3,5,9,12],
target= 9
输出: 4 解释: 9 出现在 nums中并且下标为 4
1. 二分法题目识别(应用场景)
二分的本质:根据是否满足题目的条件来缩小答案所在的区间。
二分的使用前提:有序数组,无重复元素
二分的最大优势:时间复杂度是O(logn),因此看到有序数组都要第一时间反应是否可以用二分法。
时间复杂度:时间复杂度的计算并不是计算程序具体运行的时间,而是算法执行语句的次数。
假设总共有n个元素,每次查找的区间大小就是n, n/2, n/4, ... , n/2^k, 其中k就是循环的次数,
n/2^k >= 1 (1最坏的情况,即还剩一个元素),
令n/2^k = 1, 可得 k=log2n (以2为底,n的对数)
所以时间复杂度可以表示 O(logn)
空间复杂度:空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。
因为变量只创建一次,所以空间复杂度为O(1).
2. 易错点(边界值处理)
2.1 左闭右闭
- while (left <= right) ,要用 <=, 因为left==right是有意义的
- if (nums[middle] > target) right 要赋值为 middle -1 ,因为当前这个nums[middle]不一定是target, 那么接下来要查找的左区间结束下标位置就是middle-1.
2.2 左闭右开
- while(left < right) , 要用 < ,因为left == right 在区间 [left,right) 是没有意义的
- if ( nums[middle] > target) right 更新为 middle, 因为当前 nums[middle] 不等于target , 去左区间继续寻找,而寻找区间是左闭右开区间,所以right 更新为 middle,即:下一个查询区间不会去比较 nums[middle]
3. 二分mid溢出问题
mid=(l+r)/2 时,如果 l+r 大于 INT_MAX(c++内就是int整数的上限),那么就会产生溢出问题(int类型无法表示该数),
所以写成 mid = l + (r - l) / / 2 或者 mid=l + ((r-l) >>1) 可以避免溢出问题
对于二进制的正数来说,右移x位相当于除以2的x几次方,所以右移一位等于除以2,用位运算的好处是比直接相除的操作快
4. 代码
4.1 写法一:左闭右闭
class Solution:
def search(self,nums:List[int],target:int)-> int:
left,right=0,len(nums)-1
while left<=right:
middle=left+(right-left)//2
if nums[middle] > target:
right=middle - 1 # target在左区间,所以[left,middle-1]
elif nums[middle] < target:
left=middle + 1 # target在右区间,所以[middle+1,right]
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
4.2 写法二:左闭右开
class Solution:
def search(self,nums:List[int],target:int)-> int:
left,right=0,len(nums)
while left < right:
middle=left+(right-left)//2
if nums[middle] > target:
right=middle # target在左区间,所以[left,middle)
elif nums[middle] < target:
left=middle + 1 # target在右区间,所以[middle+1,right)
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
LeetCode 27.移除元素
文档讲解:代码随想录
视频讲解:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili
题目链接:. - 力扣(LeetCode)
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素。元素的顺序可能发生改变。然后返回 nums
中与 val
不同的元素的数量。
假设 nums
中不等于 val
的元素数量为 k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。 - 返回
k
。
1.思路
数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
2.代码
2.1 暴力解法
两层for循环,一个for 循环遍历数组元素,第二个for循环更新数组。
时间复杂度:O(n^2)
空间复杂度:O(1)
class Solution:
def removeElement(self,nums:List[int],val:int)->int:
i,l=0,len(nums)
while i<l:
if nums[i]==val: #找到等于目标值的节点
for j in range(i+1,l): #移除该元素,并将后面元素向前移
nums[j-1]=nums[j]
l -= 1
i -= 1 #因为下标i以后的数值都向前移动了一位,所以i 也向前移动一位
i += 1
retrun l
2.2 双指针法(快慢指针法)
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
- 快指针:寻找新数组的元素,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
时间复杂度:O(n)
空间复杂度:O(1)
class Solution:
def removeElement(self,nums:List[int],val:int)->int:
fast=0
slow=0
size=len(nums)
while fast < size: #不加等于是因为,a=size时,nums[a]会越界
# slow用来收集不等于val的值,如果fast对应值不等于val,则把它与slow替换
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow