代码随想录算法训练营第二天
今日学习的文章链接和视频链接
参考代码随想录 https://programmercarl.com
自己看到题目的第一想法
今天三道题之前已经刷过,以为可以很顺利做出来
自己实现过程中遇到哪些困难
0977.有序数组的平方很顺利的做出来ac了,加强了数组对撞指针的理解;但是在做209.长度最小的子数组时,忘记了具体的滑动窗口的具体实现方式和关键点,比如for循环中遍历的是滑动窗口的终止位置;在做59.螺旋矩阵II时记得要用循环不变量原则,但是具体写循环时还是有点混乱,最终查笔记才写出来
今日收获,记录一下自己的学习时长
- 重新刷题用时1hr,6月30日补打卡
- 学习了字符串*2,学习时间3hr
- 开始看《图解http》,学习计算机网络
- 争取明天字符串*3,继续学习
0977. 有序数组的平方 Squares of A Sorted Array
1. 题目描述
给你一个按 非递减顺序 排序的整数数组nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums
已按 非递减顺序 排序
2. 解题思路
2.1 暴力排序
- 先遍历数组
nums
,每个元素都平方 - 然后调用库函数
sort()
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
for i in range(len(nums)):
nums[i] *= nums[i]
nums.sort()
return nums
- 时间复杂度:
O(n + nlogn)
2.2 双指针法:对撞指针
- 对于一个排序的有序数组,平方后的最大数不是在最左,就是在 最右
- 对撞指针:定义左指针
l = 0
指向最左元素,右指针r = len(nums) - 1
指向最右元素 - 初始化长度和
nums
一样的数组res
,即res = [] * len(nums)
- 遍历数组,定义遍历指针
i = len(nums) - 1
指向res
的最右边 - 左指针的平方和右指针的平方做比较,把大的那个放进
res
的最 右 边,即res[i]
- 如果
l * l > r * r
,res[i] = l * l
,左指针l
向 右 移动,l += 1
- 如果
l * l < r * r
,res[i] = r * r
右指针r
向 左 移动,l -= 1
- 遍历指针
i
向左移动,即i -= 1
,继续循环
- 如果
- 返回数组
res
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
l, r, i = 0, len(nums) - 1, len(nums) - 1
res = [] * len(nums)
while l <= r: # 出界条件;l > r
if nums[l] ** 2 > nums[r] ** 2:
res[i] = nums[l] ** 2
l += 1 # 左指针向右移动一位
else:
res[i] = nums[r] ** 2
r -= 1 # 右指针向左移动一位
i -= 1
return res
- 注意:出界条件是
l > r
, 最后一个循环时l = r
,此时最后一个元素直接进入res
; 如果循环写成while l < r
,最后一个元素遍历不到,导致res
的第一个元素没有值 - 时间复杂度:
O(n)
0209. 长度最小的子数组 Minimum Size Subarray Sum
1. 题目描述
给定一个含有n
个正整数的数组和一个正整数target
。
找出该数组中满足其和≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回0
。
示例1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例2:
输入:target = 4, nums = [1,4,4]
输出:1
示例3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
2. 解题思路
2.1 暴力循环
- 两个
for
循环遍历数组,不断寻找符合条件的子数组
具体算法:
- 定义
min_len
为无穷大,方便后续更新 - 在第一层循环中,定义子数组的和
sum = 0
,每个外循环都重置sum
- 在第二层循环中,固定子数组起始位置
i
,终止位置j
往后遍历,子数组的和sum
依次增加,判断sum
和target
大小- 如果
sum > target
,让当前符合条件的子数组长度为j - i + 1
与存储的min_len
做比较,更新min_len
为两者小的那个,即min_len = min(min_len, j - i + 1)
,找到符合条件的子数组后立刻终止循环 - 如果
sum < target
,当前子数组不符合要求,子数组终止位置j
继续向后遍历
- 如果
- 循环结束后,如果找到了符合条件的子数组,
min_len
不等于无穷大,返回min_len
,否则返回0
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
min_len = float('inf) # 定义min_len为无穷大,方便后续更新
sum = 0 # 定义子数组的和
for i in range(len(nums)):
for j in range(i, len(nums)):
sum += nums[j]
# 如果找到符合要求的子数组,把当前子数组的长度与当前min_len做比较,如果当前长度更小就更新min_len
# 继续向后遍历寻找新的复合要求的子数组
if sum >= target:
min_len = min(min_len, j - i + 1)
break
# 如果min_len不为无穷大,就返回min_len,否则返回0
return min_len if min_len != float('inf) else 0
- 时间复杂度:
O(n^2)
- 空间复杂度:
O(1)
2.2 滑动窗口
- 类似数组双指针,用一个循环做两个循环的遍历
- 滑动窗口:不断改变子数组的起始位置和终止位置,直到找到符合条件的子数组
- 窗口:和大于等于
target
的连续子数组 - 移动窗口的终止位置:用
for
循环遍历 - 移动窗口的起始位置:当子数组的和
sum
大于等于target
时,起始位置向前移动,缩小窗口
具体算法:
- 定义子数组起始位置
i = 0
和终止位置j = 0
- 定义
min_len
为无穷大,方便后续更新 - 定义子数组的和
sum = 0
- 终止位置
j
开始遍历数组,子数组的和sum
依次增加,判断sum
和target
大小- 注意:此时判断条件用循环
while
而不是if
,因为窗口会一直缩小直到不符合条件sum >= target
,如果用if
,窗口只缩小一次 - 如果
sum > target
,让当前符合条件的子数组长度为j - i + 1
与存储的min_len
做比较,更新min_len
为两者小的那个,即min_len = min(min_len, j - i + 1)
- 缩小窗口,更新子数组的起始位置
i
向后移动一位;在缩小窗口前,先更新sum
减去当前起始位置的元素,即sum -= nums[i]
,然后移动起始位置i
,终止位置j
继续向后遍历 - 如果
sum < target
,当前子数组不符合要求,终止位置j
继续向后遍历
- 注意:此时判断条件用循环
- 循环结束后,如果找到了符合条件的子数组,
min_len
不等于无穷大,返回min_len
,否则返回0
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
sum = 0
min_len = float('inf')
i, j = 0, 0
while j < len(nums):
sum += nums[j]
while sum >= target:
# 当前子数组符合条件时,更新min_len
min_len = min(min_len, j - i + 1)
# 缩小窗口,即起始位置向右移动一位
# 缩小窗口前,更新sum,然后起始位置移动
sum -= nums[i]
i += 1
# 终止位置继续向后遍历
j += 1
# 如果min_len不为无穷大,就返回min_len,否则返回0
return min_len if min_len != float('inf') else 0
- 时间复杂度:
O(n)
- 空间复杂度:
O(1)
0059. 螺旋矩阵 II Spiral Matrix II
1. 题目描述
给你一个正整数n
,生成一个包含1
到n^2
所有元素,且元素按顺时针顺序螺旋排列的n
xn
正方形矩阵matrix
。
示例1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例2:
输入:n = 1
输出:[[1]]
2. 解题思路
-
模拟 顺时针 画矩阵的过程:
- 上行 从左到右
- 右列 从上到下
- 下行 从右到左
- 左列 从下到上
-
坚持循环不变量原则,按照左闭右开,拐角处留给下一条边来画
-
二维数组表示:行列式,
matrix[][]
中第一个[]
表示 行,第二个[]
表示 列
具体算法:
- 初始化矩阵
matrix
为一个n
xn
的正方形矩阵。初始化有两种方法:matrix = [[0] * n for i in range(n)]
或matrix = [[0 for _ in range(n)] for _ in range(n)]
。 - 初始化指针
left, right, up, down
,然后填充 - 循环条件:
left
在right
之前,up
在down
之上时,即while left < right and up < down
填充矩阵 - 分别按照先 上行,再 右列,再 下行,最后 左列 的顺序填充矩阵,填充循环条件注意保持 左闭右开 ,然后四个指针更新,
left
向右,right
向左,up
向下,down
向上 - 最后注意,如果
n
为 奇数 时,此时循环条件为left == right and up == down
,中心元素不会被填充,所以需要专门填充中心元素为n ** 2
,需要判断中心元素的地址为matrix[n//2][n//2]
。如果n
为偶数,不存在中心元素,所以没有问题。
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
matrix = [[0] * n for i in range(n)] # 初始化 matrix
left, right, up, down = 0, n - 1, 0, n - 1 # 初始化四个定位指针
num = 1 # 填充的数从1开始
# 开始填充矩阵
while left < right and up < down:
# 上行从左到右
for i in range(left, right): # [0, n-1),拐角交给下一个
matrix[up][i] = num
num += 1
# 右列从上到下
for j in range(up, down): # [0, n-1),拐角交给下一个
matrix[j][right] = num
num += 1
# 下行从右到左
for k in range(right, left, -1): # [n-1, 0),拐角交给下一个
matrix[down][k] = num
num += 1
# 左列从下到上
for l in range(down, up, -1): # [n-1, 0)
matrix[l][left] = num
num += 1
# 按顺序填充一遍后,更新指针,继续遍历
left += 1 # left 向右
right -= 1 # right 向左
up += 1 # up 向下
down -= 1 # down 向上
# 最后判断n是否为奇数,填充中心元素
if n % 2 != 0:
matrix[n // 2][n // 2] = n ** 2
return matrix
- 时间复杂度:
O(n ^ 2)
遍历二维数组 - 空间复杂度:
O(n)