977.有序数组的平方
题目链接:977. 有序数组的平方 - 力扣(Leetcode)
前几天刚好又做了一遍这道题,主要思路是找到排序数组的分割点,将数组分为正、负两个部分(0或者第一个正数),然后从分割点开始向两边进行双指针遍历,较小的数优先放入结果数组。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 原做法:找到排序数组分界点,从分界点开始向两边双指针,每次选择较小的值加入结果数组
n = len(nums)
if nums[0] >= 0:
return [nums[i] ** 2 for i in range(n)]
elif nums[-1] <= 0:
return [nums[i] ** 2 for i in range(n-1, -1, -1)]
# 找到正负的分界点
for k in range(n-1):
if nums[k] * nums[k+1] <= 0:
i, j = k, k+1
break
ans = []
while i >= 0 and j < n:
if abs(nums[i]) <= nums[j]:
ans.append(nums[i] ** 2)
i -= 1
else:
ans.append(nums[j] ** 2)
j += 1
while i >= 0:
ans.append(nums[i] ** 2)
i -= 1
while j < n:
ans.append(nums[j] ** 2)
j += 1
return ans
上述解法最多要遍历两次数组,时间复杂度是O(n)。看题解发现自己蠢了,为什么非要从中间开始双指针,从首尾进行双指针异曲同工,只不过结果数组会是降序排列,返回时需要反转。当然也可以预定义结果数组,数组指针index从后往前移动。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 做法二:原数组有序,双指针从两端向中间遍历,先处理绝对值较大的数
# 可以按这个顺序构成结果数组(非递增)以后反转顺序,也可以预先构建结果数组,倒序填入
n = len(nums)
ans, index = [0] * n, n - 1
i, j = 0, n - 1
while i <= j:
if abs(nums[i]) >= abs(nums[j]):
ans[index] = nums[i] ** 2
i += 1
else:
ans[index] = nums[j] ** 2
j -= 1
index -= 1
return ans
第二种解法遍历了一遍数组,时间复杂度同样是O(n)。如果是暴力解法,就是遍历一遍数组得到每个数的平方再排序,时间复杂度是O(n+nlogn)。
209.长度最小的子数组
题目链接:209. 长度最小的子数组 - 力扣(Leetcode)
最早解题接触的就是双指针方法:
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if sum(nums) < target:
return 0
# 代码随想录,双指针解法,i遍历区间起始位置,j遍历区间终止位置
i , n = 0, len(nums)
result = float('inf')
s = 0 # 动态的区间之和
for j in range(n):
s += nums[j]
# while循环找区间起始位置
while s >= target:
# 满足条件时,记录当前区间长度
subL = j - i + 1
result = min(result, subL)
# 起始位置移动
s -= nums[i]
i += 1
return 0 if result == float('inf') else result
这次回顾题目并练习一下暴力解法和进阶部分的O(nlogn)解法。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 暴力解法
result = float('inf')
for i in range(n):
Sum = 0
for j in range(i, n):
Sum += nums[j]
if Sum >= target:
result = min(result, j - i + 1)
break
return 0 if result == float('inf') else result
# 进阶解法-> 前缀和+二分查找(数组若存在负数就不能用二分了)
n = len(nums)
result = float('inf')
preSum = [0]
for i in range(n):
preSum.append(preSum[-1] + nums[i])
# i, j之间的区间和可以表示为preSum[j] - preSum[i-1]
for i in range(n):
t = preSum[i] + target
idx = bisect.bisect_left(preSum, t)
if idx == len(preSum):
continue
result = min(result, idx - i)
return 0 if result == float('inf') else result
但二分的前提是前缀和是非单调递减的,如果数组中不全是正整数,前缀和就不满足要求,不能使用这种解法。最初我以为双指针的解法应该不会受到负数存在的影响,但举例模拟了一下发现存在负数确实会出问题,特此记录。
59.螺旋矩阵II
题目链接:59. 螺旋矩阵 II - 力扣(Leetcode)
螺旋矩阵就是在模拟转圈的过程,找到规律后比较繁琐的就是边界条件:
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
result = [[0 for _ in range(n)] for _ in range(n)]
start_x, start_y = 0, 0
offset = 1
count = 1
r = n // 2 # while循环次数
while r:
# 第一行(左闭右开)
j = start_y
while j < n - offset:
result[start_x][j] =count
count += 1
j += 1
# 最后一列
i = start_x
while i < n - offset:
result[i][j] = count
count += 1
i += 1
# 最后一行
while j > start_y:
result[i][j] = count
count += 1
j -= 1
# 第一列
while i > start_x:
result[i][j] = count
count += 1
i -= 1
# 更新边界
r -= 1
start_x += 1
start_y += 1
offset += 1
if n % 2:
# 奇数
result[start_x][start_y] = count
return result
第二天刷题任务完成,也是回顾复习为主。