1.数组理论基础
- 数组是存放在连续内存空间上的相同类型数据的集合。
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的
- 正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
- 数组的元素是不能删的,只能覆盖
2. 相关题目
704.二分查找
初始代码(时间复杂度为O(n)):
class Solution:
def search(self,nums:list[int],target:int) -> int:
for i in range(len(nums)):
if target == nums[i]:
return i
return -1
优化代码(二分法):
- 二分法前提条件:数组为有序数组,同时还强调数组中无重复元素
- 区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
1.左闭右闭即[left, right]
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
class Solution:
def search(self,nums:list[int],target:int) -> int:
left, right = 0, len(nums)-1
while(left <= right):
mid = (left+right)//2
if(target < nums[mid]):
right = right-1
elif(target > nums[mid]):
left = left+1
else:
return mid
return -1
2.左闭右开即[left, right)。
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
class Solution:
def search(self,nums:list[int],target:int) -> int:
left, right = 0, len(nums)
while(left < right):
mid = (left+right)//2
if(target < nums[mid]):
right = right
elif(target > nums[mid]):
left = left+1
else:
return mid
return -1
27. 移除元素
解法:暴力O(n^2) 快慢指针O(1)
思路:双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。
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
# 快慢指针的做法
977.有序数组的平方
- 未优化前暴力排序,时间复杂度O(n+nlogn)
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
for i in range(len(nums)):
nums[i] = nums[i] * nums[i]
nums.sort()
return nums
- 利用双指针 :
i指向起始位置,j指向终止位置。定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
i, j, k, n = 0, len(nums)-1, len(nums)-1, len(nums)
result = [-1]*n
while i<=j:
if nums[i]**2>nums[j]**2:
result[k] = nums[i]**2
i +=1
else:
result[k] = nums[j]**2
j -=1
k -=1
return result
209.长度最小的子数组
- 暴力解法双循环(时间复杂度O(n**2)超时)
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
result = 10**5
sums = 0
flag = 0
for i in range(len(nums)):
length = 0
sums = 0
for j in range(i,len(nums)):
sums += nums[j]
#print(sums)
length += 1
if (sums >= target):
if length < result:
flag = 1
result = length
#print(length)
break
if(flag == 0):
return 0
return result
- 滑动窗口优化
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
result = 10**5
sums = 0
flag = 0
j = 0
for i in range(len(nums)):
sums += nums[i]
while(sums >= target):
length = (i-j+1)
result = min(length,result)
#print(result)
sums -= nums[j]
j += 1
flag = 1
if(flag == 0):
return 0
return result
注意需写成: while(sums >= target): 不能写成if (sums >= target): 因为滑动窗口可能进行多次滑窗。
59.螺旋矩阵II
- 利用模拟思想,注意复习二分法中讲解的循环不变量原则,这里利用左闭右开的原则进行模拟
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
# 初始化要填充的正方形
matrix = [[0] * n for _ in range(n)]
left, right, up, down = 0, n-1, 0, n-1
number = 1 #要填充的数字
while left < right and up < down:
#从左到右填充
for y in range(left, right):
matrix[up][y] = number
number += 1
#从上到下进行填充
for y in range(up, down):
matrix[y][right] = number
number += 1
#从右到左进行填充
for y in range(right, left, -1):
matrix[down][y] = number
number += 1
#从下到上进行填充
for y in range(down, up, -1):
matrix[y][left] = number
number += 1
#缩小一圈
left += 1
right -=1
up +=1
down -=1
if n%2 != 0:
matrix[n//2][n//2] = number
return matrix