977.有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
初看题目挺简单的啊,直接用列表推导式生成一个新数组然后给他排序不就好了么。那么我们算一下时间复杂度生成新数组result =[x**2 for x in nums] 时间复杂度O(n) 。再用快排来排个序,时间复杂度为O(nlogn),提交一下居然AC了 return sorted(x*x for x in nums) 就是这么简单粗暴。而且这方法不管你给的nums数组有没有排序都没关系。
解题思路优化:
既然人家给你的题干中nums数组都已经排过序了,那就好好利用下,看能不能降低下时间复杂度。这个递增的有序数组包含的整数有正有负,但平方后最大的一定再数组的两端,不是最左边就是最右边,不可能是中间。这时我们就要利用相向双指针法了,i指向起始位置,j指向终止位置。定义一个新数组result,和nums数组一样的大小,让k指向result数组终止位置。
class Solution(object):
def sortedSquares(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
# 相向双指针
n = len(nums)
result = [0]*n # 初始化一个数组
i = 0 #左端指针
j = n-1 #右端指针
k = n-1 # 存放结果最大值的下标
while i<=j:
# 左右两边每比较一次就产生一个较大值存放到result中,对应的下标也就-1
# 大小相等的情况取下,取左取右都一样
if nums[i]**2 <= nums[j]**2:
result[k] = nums[j]**2
j -= 1
else:
result[k] = nums[i]**2
i += 1
k -= 1
return result
这个时间复杂度为O(n),完美
给定一个含有 n
个正整数的数组和一个正整数 target
。找出该数组中满足其总和大于等于 target
的长度最小的 子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
暴力解题
遍历出所有符合条件的子数组找出最短的,外层起始下标的遍历,内层终止下标的遍历
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
n = len(nums)
min_len = float('inf') # 初始化结果为无穷大
for i in range(n):
cur_sum = 0
for j in range(i,n):
cur_sum += nums[j]
if cur_sum>=target:
min_len =min(min_len,j-i+1)
break
return min_len if min_len<float('inf') else 0
运行一下 结果超时了,时间复杂度为O(n^2)
思路优化
其实再计算子数组的和的时候,有很多累加算法都是重复的。可以利用滑动窗口来优化
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。暴力算法的每次终止位置都是从起始位置从新遍历,故而多了很多重复计算。
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
n = len(nums)
left = 0
right = 0
min_len = float('inf')
cur_sum = 0 #当前的累加值
while right < n:
cur_sum += nums[right]
while cur_sum >= target: # 当前累加值大于目标值
min_len = min(min_len, right - left + 1)
cur_sum -= nums[left]
left += 1
right += 1
return min_len if min_len != float('inf') else 0
- 时间复杂度:O(n)
- 空间复杂度:O(1)
为什么时间复杂度是O(n)。
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
59.螺旋矩阵II
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
初看题目感觉一脸懵逼,无从下手啊,那就用笔在纸上画几个图吧,当n=1,2,3,4,5,6时候,似乎找到一些规律
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
每循环填充一圈就需要填充这4条边,接下来的重点就是定义每条边的边界,这里统一采用左闭右开(这样两条边的交点处才能填充,不然就会发生重复填充的现象)。以n=5为例,第一次循环每条线包含4个数字填充了4*4=16个数字,第二次循环每条线包含2个数字填充了2*4=8个数字,结果最中间留了一个最大的数字25。如果n=4 第一次循环填充3*4个数字,第二次填充1*4个数字,总共16个数字 结束。从中总结出规律,完善代码
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
nums = [[0] * n for _ in range(n)] # 初始化二维数组
startx, starty = 0, 0 # 起始点
loop, mid = n // 2, n // 2 # 迭代次数、n为奇数时,矩阵的中心点
count = 1 # 计数
for offset in range(1, loop + 1) : # 每循环一层偏移量加1,偏移量从1开始
for i in range(starty, n - offset) : # 从左至右,左闭右开
nums[startx][i] = count
count += 1
for i in range(startx, n - offset) : # 从上至下
nums[i][n - offset] = count
count += 1
for i in range(n - offset, starty, -1) : # 从右至左
nums[n - offset][i] = count
count += 1
for i in range(n - offset, startx, -1) : # 从下至上
nums[i][starty] = count
count += 1
startx += 1 # 更新起始点
starty += 1
if n % 2 != 0 : # n为奇数时,填充中心点
nums[mid][mid] = count
return nums
补充 :如果二维数组初始化的值定义成n^2,则最后的一段判断为奇数的代码则可以省略了
总结
通过前面两个案例 理解了相向双指针的算法应用以及滑动窗口算法的应用,第三题则是对整体思维逻辑的处理以及代码的整体掌控