977.有序数组的平方
文章链接:977.有序数组的平方 - 力扣(LeetCode)
这是我写的程序:
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 数组为长度为1的情况
if len(nums) == 1:
nums[0] = nums[0]**2
return nums
# 将原数组平方
for i in range(len(nums)):
nums[i] = nums[i]**2
# 找到最小值的坐标
start = 0
while nums[start]>=nums[start+1]:
start += 1
if start == len(nums)-1: break
left = 0
rightSmall, rightBig = start, start+1
right = rightSmall
numsNew = [0]*len(nums) # 定义新数组
if rightBig == len(nums):
right = rightBig
for i in range(len(numsNew)):
numsNew[i] = nums[len(nums)-1-i]
while right >= 0 and right < len(nums) :
if nums[rightSmall] <= nums[rightBig]:
numsNew[left] = nums[rightSmall]
rightSmall -= 1
right = rightSmall
elif nums[rightSmall] > nums[rightBig]:
numsNew[left] = nums[rightBig]
rightBig += 1
right = rightBig
left += 1
# 跳出循环,即代表指针right超出了数组下标的范围,分情况特殊处理(某一方超出不代表另一边也到达了边界)
if rightSmall < 0:
while left<len(nums):
numsNew[left] = nums[rightBig]
left += 1
rightBig += 1
elif rightBig > len(nums)-1:
while left<len(nums):
numsNew[left] = nums[rightSmall]
left += 1
rightSmall -= 1
return numsNew
运用了双指针的思想,但我最初的思路是从中间的最小值往两边遍历,比较左右值的大小,小者则进入新数组,程序调试了很久,加了许多判定条件最终才全部通过了。
代码随想录中的程序(双指针法):
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 双指针法
for i in range(len(nums)):
nums[i] = nums[i]**2
slow = len(nums)-1
fastSmall, fastBig = 0, len(nums)-1
numsNew = [0]*len(nums)
while fastSmall <= fastBig:
if nums[fastSmall] > nums[fastBig]:
numsNew[slow] = nums[fastSmall]
fastSmall += 1
else:
numsNew[slow] = nums[fastBig]
fastBig -= 1
slow -= 1
return numsNew
这是参考了代码随想录的程序,他的思路是从两边往中间遍历,最终跳出循环的条件为:fastSmall > fastBig,即“左边”的值不再小于等于“右边”的值,这样代码就简洁很多了,果然写算法题还是思路最重要!
209.长度最小的子数组
题目链接:209.长度最小的子数组 - 力扣(LeetCode)
这是我写的程序(暴力解法):
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 暴力算法
for i in range(1, len(nums)+1):
for j in range(len(nums)+1-i):
sum = 0
for k in range(i):
sum += nums[j+k]
if sum >= target: return i
return 0
两层遍历,第三个for是将窗口内的值累加,程序由于运行超时而没有全部通过。
代码随想录中的程序(滑动窗口):
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 滑动窗口
i= 0 # 滑动窗口起始位置
result = float("inf") # 定义一个无限大的数
sum = 0 # 滑动窗口数值之和
for j in range(len(nums)):
sum += nums[j]
while sum >= target:
length = j-i+1
result = min(result, length)
sum -= nums[i]
i += 1
return 0 if result == float("inf") else result
滑动窗口的思想很巧妙。(以下引用自算法训练营中的总结)
滑动窗口:本质是满足了单调性,即左右指针只会往一个方向走且不会回头。收缩的本质即去掉不再需要的元素。也就是做题我们可以先固定移动右指针,判断条件是否可以收缩左指针算范围。
滑动窗口的原理是右边先开始走,然后直到窗口内值的总和大于target,此时就开始缩圈,缩圈是为了找到最小值,只要此时总和还大于target,我就一直缩小,缩小到小于target为止在这过程中不断更新最小的长度值,然后右边继续走,如此反复,直到右边碰到边界。这样就保证了可以考虑到最小的情况。
59.螺旋矩阵II
这道题感觉有点难上手,直接看的视频,再参考代码随想录写的程序。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
nums = [[0] * n for _ in range(n)] # 生成一个二维数组
startx, starty = 0, 0 # 起始位置x和y
loop = n // 2 # 圈数
count = 1 # 计数
for offset in range(1, loop+1): # offset表示偏移量,每转一圈偏移量得增加1
for j in range(starty, n-offset):
nums[startx][j] = count
count += 1
for i in range(startx, n-offset):
nums[i][n-offset] = count
count += 1
for j in range(n-offset, starty, -1):
nums[n-offset][j] = count
count += 1
for i in range(n-offset, startx, -1):
nums[i][starty] = count
count += 1
startx += 1
starty += 1
offset += 1
mid = n // 2 # 若n为奇数,则需求最中心点(mid)
if (n%2) != 0:
nums[mid][mid] = count
return nums
需注意的点:
- 此题要坚持循环不变量原则,区间的定义就是不变量,上面程序采用的是左闭右开的区间定义;
- nums = [[0] * n for _ in range(n)] 为生成一个二维数组,_ 只是一个占位符,只在乎遍历次数range(n),即遍历n次;
- range(大的数, 小的数),这样写是错误的,应为 range(大的数, 小的数, -1)。
range(start, stop[, step]) :
start:计数从 start 开始,默认是从 0 开始,例如,range(6) 等价于 range(0, 6);
stop:计数到 stop 结束,但不包括 stop,例如,range(0, 6) 是 [0, 1, 2, 3, 4, 5] (不包括6);
step:步长,默认为1,递减应为负数,例如:range(0, 6) 等价于 range(0, 6, 1),range(6, 0, -1)是 [6, 5, 4, 3, 2, 1] (不包括0)。