今日总结:
- 我会用列表推导法啦!
- 不用太在意超过xx人,只要自己分析的出来算法复杂度就可以了!
- 滑动窗口也可以理解为双指针法的一种,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
- 注意python的引用,建议初始化列表只用一种方法。
1. 有序数组的平方977
视频讲解:《代码随想录》算法视频公开课 (opens new window):双指针法经典题目!LeetCode:977.有序数组的平方 (opens new window)
状态:先用暴力法做出来了一遍,然后尝试用了双指针和pop(没做出来,就差一步,不用pop就行)
题目如下——
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
- 输入:nums = [-4,-1,0,3,10]
- 输出:[0,1,9,16,100]
- 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
示例 2:
- 输入:nums = [-7,-3,2,3,11]
- 输出:[4,9,9,49,121]
【要素察觉】双指针
- 有序数组
- 如果存在负数,中间值最小
【代码实现】
(暴力)先平方再排序
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 暴力法
res = [num * num for num in nums]
res.sort() # 任意排序算法皆可
return res
(双指针)while
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
l, r, i = 0, len(nums)-1, len(nums)-1
res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果
while l <= r:
if nums[l] ** 2 < nums[r] ** 2: # 左右边界进行对比,找出最大值
res[i] = nums[r] ** 2
r -= 1 # 右指针往左移动
else:
res[i] = nums[l] ** 2
l += 1 # 左指针往右移动
i -= 1 # 存放结果的指针需要往前平移一位
return res
2. 长度最小的子数组209
力扣题目链接(opens new window)https://leetcode.cn/problems/remove-element/)
视频讲解:《代码随想录》算法视频公开课 (opens new window):拿下滑动窗口! | LeetCode 209 长度最小的子数组
状态:自己做出来的!滑动窗口!
题目如下——
给定一个含有
n
个正整数的数组和一个正整数target
。找出该数组中满足其总和大于等于
target
的长度最小的 连续 子数组
[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。**如果不存在符合条件的子数组,返回0
。
【要素察觉】滑动窗口
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。
在本题中实现滑动窗口,主要确定如下三点:
- 窗口内是什么? -> 满足其和 ≥ s 的长度最小的 连续 子数组。
- 如何移动窗口的起始位置? -> 如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
- 如何移动窗口的结束位置?-> 窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
思路一:暴力解法
思路二:快慢指针
【代码实现】
(暴力解法)for + for循环,只要不符合条件就break
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
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 >= s:
min_len = min(min_len, j - i + 1)
break
return min_len if min_len != float('inf') else 0
(滑动窗口) 本质是双指针,改变了索引位置
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
l = len(nums)
left = 0
right = 0
min_len = float('inf')
cur_sum = 0 #当前的累加值
while right < l:
cur_sum += nums[right]
while cur_sum >= s: # 当前累加值大于目标值
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
【练习题】
3. 螺旋矩阵II 59
视频讲解:《代码随想录》算法视频公开课 (opens new window):拿下螺旋矩阵!LeetCode:59.螺旋矩阵II
状态:初始化有问题;用了20min,一开始会陷入超过时间限制的问题。
题目如下——
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
【思路】模拟
面试中出现频率较高的题目,本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。
大家还记得我们在这篇文章数组:每次遇到二分法,都是一看就会,一写就废 (opens new window)中讲解了二分法,提到如果要写出正确的二分法一定要坚持循环不变量原则。
而求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人。
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
【代码实现】
(直接按照思路模拟)
class Solution:
def generateMatrix(self, n: int) -> 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
[注意] python语言的引用问题
初始化认准:[0 for _ in range(n)]
,别的会共用地址。