文档讲解:代码随想录 二分查找、代码随想录 移除元素
视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找、 数组中移除元素并不容易! | LeetCode:27. 移除元素
力扣题目链接
题目描述:给定一个n
个元素有序的(生序)整型数组nums
和一个目标值target
,写一个函数搜索nums
中的target
,如果目标值存在返回下标,否则返回-1
。
示例1:
输入:
nums
= [-1,0,3,5,9,12],target
= 9
输出:4
解释:9出现在nums
中且下标为4
示例 2:
输入:
nums
= [-1,0,3,5,9,12],target
= 2
输出: -1
解释: 2 不存在nums
中因此返回 -1
提示:
nums
中所有元素是不重复的。n
将再[1, 10000]
之间。nums
的每个元素都将在[-9999, 9999]
之间。这里定义target在一个左闭右闭的区间里,[left, right],需要注意:
while left <= right
这里要用<=
,因为left == right
时区间[left, right]
也是有意义的;if nums[mid] > target: right = mid - 1
,这里要给right
赋值mid - 1
,因为此时nums[mid]
一定不是target
,那么闭区间的右界从mid - 1
就可以了代码如下:
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
right = mid - 1
else:
left = mid + 1
return -1
这里定义target在一个左闭右开的区间里,[left, right),需要注意:
right = len(nums)
,这里不是len(nums) - 1
,因为这里是左闭右开区间,右界不包含在查找范围内;while left < right
这里要用<
,因为left == right
时,[left, right)
是一个空的区间,没有意义;if nums[mid] > target: right = mid
,这里给right
赋值mid
,因为区间是右开的,所有右界赋值的mid
是不包含在内的,若赋值为mid - 1
则会漏掉nums[mid - 1]
是否为target
的判断。代码如下:
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)
while left < right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
right = mid
else:
left = mid + 1
return -1
力扣题目链接
题目描述:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums 为 无重复元素 的 升序 排列数组
-10^4 <= target <= 10^4
其实二分法的思路还是很清晰的,唯一就是如果数组没有target
,返回的下标是什么。其实while
终止的时候一定是left > right
了,此时left
指向的是比target
小的数的右边一位,right
指向的是比target
大的数的左边一位,说明此时nums[right]
和nums[left]
中间刚好是需要插入的位置,并且此时left = right + 1
,所以插入的位置为left
或right + 1
代码如下:
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
return mid
return right + 1
力扣题目链接
题目描述:给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O ( 1 ) O(1) O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例1:
输入:
nums
= [3,2,2,3],val
= 3
输出:2,nums
= [2,2]
解释:函数应该返回新的长度 2, 并且nums
中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而nums
= [2,2,3,3] 或nums
= [2,2,0,0],也会被视作正确答案。
示例 2:
输入:
nums
= [3,2,2,3],val
= 3
输出: 2,nums
= [2,2]
解释: 函数应该返回新的长度 2, 并且nums
中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而nums
= [2,2,3,3] 或nums
= [2,2,0,0],也会被视作正确答案。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
本题最直接的方法就是暴力解法,两层for循环,一层for循环遍历数组,当遇到要删除的值时,另一层for循环更新数组。
代码如下:
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 += 1
return l
快慢指针:通过一个快指针和一个慢指针在一个for循环下完成两个for循环的工作。
删除过程如下(gif来源于代码随想录):
代码如下:
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
slow = 0
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow
今日毕,后面补上相关题目推荐。