代码随想录第一天数组part01|704.二分查找、移除元素
数组理论基础
数组类题目,在思维上不难,主要考察对代码的掌控能力,即想法简单,但难以实现。
数组是存放在连续内存空间上的相同类型数据的集合,可以方便的通过下标索引的方式获取到下标下对应的数据。正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。数组的元素是不能删的,只能覆盖。
对于二维数组,不同编程语言的内存管理是不一样的,C++中二维数组是连续分布的,而Java不是。
704.二分查找
思路:有序数组,数组中无重复元素,这些都是使用二分法的前提条件,(一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的)
二分法的关键:区间的定义:左闭右闭,左闭右开?(两种写法)
1)左闭右闭 [left, right]
注意两点
:
while遍历的需要是一个合法的区间 left<=right
下一个搜索区间不包括middle right = middle-1 left = middle+1
伪代码
:
left=0 right=numsize-1
while left<=right:
middle = (left-right)/2
if nums[middle] > target: // target在left与middle之间
right = middle-1
elif nums[middle] < target: // target在middle与right之间
left = middle+1
else:
return middle
return -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
elif nums[middle]<target:
left = middle+1
else:
return middle
return -1
2)左闭右开[left,right)
注意两点
:
while遍历的需要是一个合法的区间 left<right
下一个搜索区间包括middle right = middle left = middle+1
伪代码
:
left=0 right=numsize
while left<right:
middle = (left-right)/2
if nums[middle] > target: // target在left与middle之间
right = middle //右开
elif nums[middle] < target: // target在middle与right之间
left = middle+1 //左闭
else:
return middle
return -1
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
elif nums[middle]<target:
left = middle + 1
else:
return middle
return -1
二分的最大优势是在于其时间复杂度是O(logn)
关于二分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,用位运算的好处是比直接相除的操作快
27.移除元素
数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
暴力解法
两层循环,第一层循环找要删除的数组元素,第二层for循环把后面的元素一个一个向前覆盖。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
i,n = 0, len(nums)
while i<n:
if nums[i] == val:
for j in range(i,n-1):
nums[j] = nums[j+1]
n-=1
i-=1 # 因为下标i以后得数值都向前移动了一位,所以i也向前移动一位
i+=1
return n
时间复杂度:O(n^2)
空间复杂度:O(1)
双指针法
用一层for循环完成两层for循环要做的事情
定义快指针、慢指针
快指针:寻找新数组中所需要的元素(删除目标元素后的数组)
慢指针:指向要更新的数据下标位置
for (fast=0;fast<numsize;fast++)
if (nums[fast]!=val)
nums[slow] = nums[fast]
slow++
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
时间复杂度:O(n)
空间复杂度:O(1)