977. 有序数组的平方
- 暴力法:时间复杂度O(n+nlogn),是遍历平方+排序。空间复杂度O(1),如果排序不需要额外空间的话。(python自带的sort是怎么实现的?)
- 双指针:时间复杂度O(n),空间复杂度O(n)。空间换时间?
class Solution(object):
def sortedSquares(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
# 1.遍历平方,O(n),排序O(nlogn)——但这样的话就没有用到原数组的顺序
for i in range(len(nums)):
nums[i] *= nums[i]
nums.sort() # 本地修改?
return nums
# 2.列表推导法
return sorted(x*x for x in nums)
# 3. 双指针?一个在前,一个在后,向中间靠近.时间复杂度O(n),空间复杂度O(n)
length = len(nums)
left = 0
right = length - 1
res = [float('inf')] * length # 需要提前定义列表,存放结果
for i in reversed(range(length)):
if abs(nums[left]) < abs(nums[right]):
res[i] = nums[right] **2
right -= 1
else:
res[i] = nums[left] ** 2
left += 1
return res
209. 长度最小的子数组
- 暴力法,时间复杂度O(n2),python运行的话leetcode会超出时间限制。
(1)一开始想的是外循环是子序列长度的大小,从1到L;内循环是子序列开始的数字;这样计算cur_sum的时候还得加一个长度为L,时间复杂度为O(n2L),最大的时候是O(n3)!!!后来想到用一个数组保存子序列长度为l-1的来计算子序列长度为l的,但这样相当于用O(n)的空间复杂度换了O(L)的时间复杂度,也不是很划算。
(2)后来看题解,暴力解法的外循环是子序列开头;内循环是子序列结尾,这样直接可以用O(1)挨个计算1-L长度的子序列的sum,直接省掉了前面O(L)的时间复杂度或者说O(n)的空间复杂度,感觉自己是个傻子。。 - 移动窗口法:感觉也就是双指针法。外循环是子序列的结尾,也就是快指针;慢指针是子序列的开头。两个指针的中间就是子序列。当子序列的sum小于target时,子序列长度不够,因此快指针不断右移;当子序列的sum大于等于target时,表示子序列长度够了,记录下此时的min-len,再缩短子序列直到sum小于target,此时移动的是慢指针。时间复杂度为O(n),相当于快慢指针各遍历了一遍数组。
- 数组如果有负的,就不能使用这个方法了吧。那除了暴力还有什么方法呢???
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
# 滑动窗口法
left = 0
right = 0
cur_sum = 0
cur_len = 0
min_len = float('inf')
for i in range(len(nums)): # 表示窗口的结束点
cur_sum += nums[i]
cur_len += 1
while(cur_sum >= target):
min_len = min(min_len, cur_len) # cur-sum大于等于target,更新窗口大小
cur_sum -= nums[left] # 一旦cur-sum小于target,则退出循环了
left += 1 # 减小窗口
cur_len -= 1
return 0 if min_len == float('inf') else min_len
# 暴力法
# python用暴力法好像不行?
l = len(nums)
min_len = float('inf')
for i in range(l):
cur_sum = 0
for j in range(i, l):
cur_sum += nums[j]
if cur_sum >= target:
min_len = min(min_len, j-i+1)
break
return 0 if min_len == float('inf') else min_len
# 长度最小的连续子数组
# 窗口从0到l-1遍历,暴力法O(n2*l)
# 记录下来前一个计算的结果的话,也就是O(n2),没有提高很多啊?而且空间复杂度变成O(n)了
length = len(nums)
# O(n)
res = [0] * length
for l in range(1, length+1): # l表示的是窗口长度,[1,... ,length]
# O(n)
for i in range(length-l+1): # [0, 1, ..., length-1-l]
add_l = res[i] + nums[i+l-1]
# O(l)
res[i] = add_l
if add_l >= target:
return l
return 0
59. 螺旋矩阵II
- 首先定义开闭区间,采用左闭右开的方式最符合直觉。
- 外循环为offset偏置,[0, mid],mid=n//2。在每一个offset下,有四个方向的循环,从左到右,从上到下,从右到左,从下到上。此时左闭右开就能完全覆盖一圈。但需要好好琢磨range的大小起点终点,以及matrix的坐标。
- ps:赋值的时候不能采用[[0]*n]*n,群里给出的解答:[[0]*n]*n 事实上是创建了一个包含 n 个指向同一行引用的列表。你改一个地方,所有的行都会跟着修改。(但是我不理解为什么内层的[0]*n不会被最后的赋值覆盖)
- 群里给出的解答:大概意思是一开始的[0]*n是创建一个列表,创建数组是一个分配连续空间的过程,这个不可能是复制一个[0]这样一个int空间。
- 但是[[0]*n]*n相当于复制上面创建的这个列表,而不是在新建列表,因此只是生成了指向已创建空间的指针。而for _ in range(n)相当于在一个循环里生成了n个列表。
- 说实话,感觉是个python定义数组里的坑。总的来说,数组n是为了定义n个copy,数字n是为了定义一个列表。
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
matrix = [[0]*n for _ in range(n)] # [[0]*n]*n这样的定义方式好像会出问题
start_x, start_y = 0, 0
mid, loop = n//2, n//2
count = 1
# 左闭右开
for offset in range(1, loop+1): # 偏置??但是这个偏置和startx的关系?
for i in range(start_y, n-offset): # 左闭右开,n-1取不到
matrix[start_x][i] = count
count += 1
print(matrix)
for i in range(start_x, n-offset):
matrix[i][n-offset] = count
count += 1
print(matrix)
for i in range(n-offset, start_y, -1):
matrix[n-offset][i] = count
count += 1
for i in range(n-offset, start_x, -1):
matrix[i][start_y] = count
count += 1
start_x += 1
start_y += 1
if n%2 != 0: # n是奇数
matrix[mid][mid] = count
return matrix
数组总结
总的来说,数组部分题目使用到的一些trick也就:
- 二分查找
- 双指针
- 滑动窗口(变相双指针)