704.二分查找
题目
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
原始思路
class Solution(object):
def search(self, nums, target):
j=0
for i in range(len(nums)):
if target==nums[i]:
return i
else:
j+=1
if j==len(nums):
return -1
二分法流程
由腾讯元宝得到:
二分法(又称折半法)是一种在有序数组中查找目标值的高效算法。它的基本流程如下:
1. 初始化:设定两个指针,分别为 low 和 high,初始值分别为数组的起始索引(0)和结束索引(数组长度减1)。
2. 检查搜索范围:当 low <= high 时,执行第 3 步;否则,停止搜索,表示目标值不在数组中。
计算中间索引:mid = (low + high) // 2 (整数除法)。
3. 比较目标值与中间元素:
a. 如果目标值等于中间元素(arr[mid] == target),则找到目标值,返回 mid。
b. 如果目标值小于中间元素(arr[mid] > target),则目标值位于左侧子数组,更新 high = mid - 1。
c. 如果目标值大于中间元素(arr[mid]< target),则目标值位于右侧子数组,更新 low = mid + 1。
4. 重复第 2-4 步,直至找到目标值或搜索范围为空。
这种算法的优势在于每次迭代都将搜索范围缩小一半,因此时间复杂度较低,为 O(log n),其中 n 为数组长度。
尝试利用二分法编写代码
class Solution(object):
def search(self, nums, target):
low=0
high=len(nums)-1
while low<=high:
mid=(low+high)//2
if nums[mid]==target:
return mid
elif nums[mid]<target:
low=mid+1
else:
high=mid-1
return -1
代码随想录学习
运用二分法的前提是该数组为有序,无重复元素的数组。否则,找不到下标或下标有多个。另外就是注意根据(low,high)的取值主要分为两种情况左闭右闭、左闭右开。而这两种情况又会影响到判断条件中是否取等以及low,high的更新值。
左闭右闭情况
如上
左闭右开情况
class Solution(object):
def search(self, nums, target):
low=0
high=len(nums)
while low<high:
mid=(low+high)//2
if nums[mid]==target:
return mid
elif nums[mid]<target:
low=mid+1
else:
high=mid
return -1
27.移除元素
题目
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素。元素的顺序可能发生改变。然后返回 nums
中与 val
不同的元素的数量。
原始思路
class Solution(object):
def removeElement(self, nums, val):
j=0
for i in range(len(nums)):
k=i-j
if val==nums[k]:
nums.pop(k)
j+=1
return j,nums
但是报错显示:
TypeError: [2, 2] is not valid value for the expected return type integer[] raise TypeError(str(ret) + " is not valid value for the expected return type integer[]");
应该是返回错误,但是不知道怎么修改。
代码随想录学习
数组中元素的内存地址是连续的,元素被删除后位置还是空在那里的,还需要将后面的元素往前移。所以是要两个循环,外层循环遍历数组,内层循环向前覆盖元素。还有一种是双指针法,下面会具体介绍。
暴力法
class Solution(object):
def removeElement(self, nums, val):
j=0
for i in range(len(nums)):
if nums[i]!=val:
nums[j]=nums[i]
j+=1
return j
以上是用户评测内容可知,leetcode平台实际上的验证过程是首先调用removeElement函数,获取返回的长度j,验证nums数组的前j个元素是否符合预期。
leetcode不会直接检查返回的值,而是使用这个长度来验证修改后的数组内容是否正确所以这也解释了为什么一开始的代码报错,提示类型错误。
整个过程如下:
removeElement函数遍历输入的列表nums,并将所有不等于val的元素移动到数组的前面,同时保持它们的相对顺序。j用于跟踪新数组的长度,即移动了多少个不等于val的元素。函数返回j,表示新数组的长度。在这个过程中,nums数组被就地修改。前j个元素就是新数组的内容。leetcode会根据返回的新数组长度j来检查nums数组的前j个元素。
-----------------------------------------------------------------------------------------------
还是要认真读题
-----------------------------------------------------------------------------------------------
双指针法
基本思想
双指针法通过同时操作两个指针,减少时间复杂度。通常适用于以下几种情况:
1.遍历一半数组:如左右指针,从数组两端向中间移动。
2.查找特定条件:如快慢指针1,一个指针以较快速度移动,另一个以较慢速度移动。
3.有序数组或链表:用两个指针寻找满足条件的元素对。
代码
快慢指针法
class Solution(object):
def removeElement(self, nums, val):
fast = 0
slow = 0
size = len(nums)
while fast < size:
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
双向指针法
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
n = len(nums)
left, right = 0, n - 1
while left <= right:
while left <= right and nums[left] != val:
left += 1
while left <= right and nums[right] == val:
right -= 1
if left < right:
nums[left] = nums[right]
left += 1
right -= 1
return left
解释如下:
函数接收两个参数:一个整数列表nums和一个整数val,返回一个整数。初始化两个指针left和right,分别指向数组的开始和结束位置。主循环使用while循环,当left指针小于或等于right指针时继续执行,确保了所有元素被检查和处理。内部的while循环用于移动left指针,跳过所有不等于val的元素,直至找到一个等于val的元素或指针超出right。内部的while循环用于移动right指针,跳过所有不等于val的元素,直至找到一个等于val的元素或指针超出left。如果left指针小于right指针,交换两个指针处的元素,并同时移动left和right指针,以继续处理下一个元素。当主循环结束时,返回left指针的值作为新数组的长度。