前言:无意发现Leetcode有时间和控件复杂度分析,好耶!
977.Squares of a Sorted Array | Link
核心词:双指针
方法一:暴力(自己想的)
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
nums2 = []
for e in nums:
nums2.append(e*e)
nums2.sort()
return nums2
# 暴力
# Time: O(nlogn)
方法二:双指针(也是自己想的)
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
left = 0
right = len(nums) - 1
result = [None]*len(nums)
back_index = len(nums) - 1
while(left <= right):
if abs(nums[left]) <= abs(nums[right]):
result[back_index] = nums[right]*nums[right]
back_index -= 1
right -= 1
else:
result[back_index] = nums[left]*nums[left]
back_index -= 1
left += 1
return result
# Time: O(n)
# 自己想的双指针
- 我首先用了暴力方法,对原数组平方以后,调用排序函数。复杂度O(nlogn).
- 推荐的解法是双指针
- 本题引入新的结果空间result,属于是“空间换时间”
- 本题题干说,“non-decreasing order”,这类有序的条件必有用。考虑下双指针。
- 注意创建空的固定长度list方法:result = [None]*len(nums)
- 2504刷题,通过本题和上一题,我发现一个自己的思考缺陷,就是一旦涉及双指针题目,只考虑用while循环,但while循环需要(在不止一处加上)对越界的判断,很麻烦。其实用for循环会很简单。
209.Minimum Size Subarray Sum | Link
Key Words: 滑动窗口(本质也是双指针),双循环但复杂度O(n)
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
i = 0
sum = 0
result = len(nums) + 1
for j in range(len(nums)):
sum += nums[j]
while sum >= target:
result = min(j-i+1,result)
sum -= nums[i]
i += 1
if result == len(nums) + 1:
return 0
else:
return result
# 双指针法
# 看似两个循环,但实际满打满算只循环2轮,即O(2n) = O(n)
- 遍历的j代表启示指针or终点指针?只能是终点,不然与暴力解一样了。
- 循环条件中,若i j没有相等的话,则循环最后i j一样的那个元素丢失(未处理)。
- 精彩!两个for,时间复杂度居然是O(n):他两个指针满打满算一共就跑了2n次啊,但是你用暴力解法每一个i就要重新跑一次j,自然就是n的平方
- [网友] 怎么说呢,滑动窗口本质上也是一种双指针法,是在充分理解题目的情况下,暴力算法的一种简化。
这道题之所以可以使用滑动窗口,很重要的一个原因是,在移动终止位置的时候,初始位置是不可逆的,初始位置只可能往后移动,而不用每次都从第零个元素开始。- 这里”初始位置“指的是:终止位置(终止指针)的每次进行循环前开始的位置。
- 所有双指针法,都是充分利用题目的一个隐藏的特征,来对暴力算法的一种简化
- 视频中,老师说result初始值设为MAX,其实不必,设为len(nums) + 1即可
- 最后return可以用Python的三目运算符简化:
return 0 if result == len(nums) + 1 else result
59.Spiral Matrix II | Link
Key Points: 循环不变量原则,循环的边界条件
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
nums = [[0] * n for _ in range(n)]
startx,starty = 0,0
offset = 1
val = 1
loop = n // 2
for offset in range(1, loop + 1) :
for j in range(starty,n-offset):
nums[startx][j] = val
val += 1
for i in range(startx, n-offset):
nums[i][n-offset] = val
val += 1
for j in range(n-offset,starty, -1):
nums[n-offset][j] = val
val += 1
for i in range(n-offset,startx, -1):
nums[i][starty] = val
val += 1
startx += 1
starty += 1
if n%2 == 1:
nums[n//2][n//2] = val
return nums
- 本题每次做,每次都做不成功。中途看了3次老师的代码。
- 循环不变量 原则。该方法可以简化代码/边界条件。本题中,循环==转一圈,不变量==对边界的处理方式(指开闭)
- 我最初的想法都是对的,除了对奇数行的处理:单拎出来赋值。
- 本题必须画图分析。
- 一些Python语法不熟悉:
- 创建值为0的 n*n list:
nums = [[0] * n for _ in range(n)]
- 创建值为0的 n*n list:
- range从大到小,要有-1:
range(10,1, -1)
数组总结关键点:
- 数组是存放在连续内存空间上的相同类型数据的集合。
- 因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
- Java的二维数组在内存中不是
3*4
的连续地址空间,而是四条连续的地址空间组成!