今日碎碎念:坚持打卡第二天,争取多刷一些题。
977.有序数组的平方
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解:代码随想录
视频讲解: 双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili
关键在于理解双指针的思想
题目:
给你一个按 非递减顺序 排序的整数数组 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]
思路:
-
-
根据题意,可以先在原数组的基础上直接进行平方,平方后的数值存储在原数组的位置得到一个新数组,然后再将新数组进行排序。(暴力解法)
-
利用双指针的思想。根据题意,该数组是有序的,因此其平方后,最大值和最小值存在于数组的两端。因此可以定义一个对撞指针i,j。i指向开始的位置,j指向种植位置。随后,重新再定义一个新数组,比较两个指针所指位置的平方值较大的放入新数组中。
即:
如果
A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
。如果
A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
。
-
算法:对撞指针法(见第一天的第二个算法介绍)
题解:
-
暴力解法:
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
for i in range(len(nums)):
nums[i] *= nums[i]
nums.sort()
return nums
-
对撞双指针法:
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 对撞指针法
# 第一步:定义指针。这里需要三个,分别指向开始(i)、结尾(j)以及新数组(k),新数组从大到小排列
i, j, k = 0, len(nums)-1, len(nums)-1 #新数组的长度和现在长度一样
# 定义一个新数组,长度和原来相等的空数组
result = [float("inf")] * len(nums)
#result =[] #list结合append()方法不管用,输出空值
while i <= j:
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
-
列表推导式的方法
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
return sorted(x*x for x in nums)
总结:
这道题还是比较容易的,不管是暴力解法、列表推导式,还是双指针方法,只要想清楚实现步骤以及需要的东西,很容易就解出来了。
209.长度最小的子数组
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解:代码随想录
视频讲解:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili
关键在于理解滑动窗口
题目:
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4] 输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1] 输出:0
思路:
首先寻找所有组合,然后在所有的组合中寻找符合题意条件的组合,并计算符合条件的组合数量。可以使用滑动窗口的思想来编写代码。
那什么是滑动窗口思想呢?
算法:滑动窗口思想
关键:如何移动起始位置。简单来说就是,从起始位置0开始固定一个指针,然后另外一个指针遍历剩余的所有数来寻找符合条件的数组集合。后面以此类推,当一个位置的固定指针和剩下的滑动指针遍历完成后,才能固定下一个指针继续遍历。
可以简单用小学数学中寻找一组元素中有多少种组合方式,并且从这些组合方式中寻找出符合条件的组合的思路来理解。
在具体编写代码的过程中,需要注意如下几点:
-
根据题意确定窗口内是什么内容
-
如何移动窗口的起始指针位置
-
如何移动窗口的结束位置
题解:
-
滑动窗口
-
在本题中,窗口中的内容是满足>=target的长度最小的连续子数组
-
窗口移动的起始指针:当窗口中的值大于target,则起始固定指针需要移动
-
窗口移动的结束位置:遍历起始指针后的所有位置。
-
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 1.获取数组的长度
l = len(nums)
# 2.确定固定指针和滑动指针的初始位置
left = 0 # 固定的指针
right = 0 # 滑动的指针
# 3.确定符合条件子数组的初始长度和当前子数组的累加和
min_len = float("inf")
cur_sum = 0 #当前子数组的累加和
# 4.滑动窗口思想寻找符合条件的子数组
while right < l: # 不能滑动出数组
cur_sum += nums[right] # 当前累加和等于所有滑动指针走过的值相加
while cur_sum >= target:
min_len = min(min_len, right-left + 1) # right-left + 1 防止运算出界。和之前的最小长度比较,直到寻找到最小的
cur_sum -=nums[left] # 当前的和减去固定指针之前的,寻找出最小子数组的和
left += 1
right += 1
return min_len if min_len != float("inf") else 0
-
暴力解法:两个for循环(leetcode超出时间限制)
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 1.获取数组的长度
l = len(nums)
# 2.确定符合条件子数组的初始长度
min_len = float("inf")
# 3.暴力求解
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 min_len if min_len != float("inf") else 0
总结:
很有意思的一种代码书写方法,需要多理解。
相似练习题&题解
904.水果成篮
-
思路: 寻找最长的子数组,并且计算子数组中的数值种类,返回最长的子数组。
-
具体的代码书写有点绕
-
题解:
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
# 确定指针
left = 0
# 确定返回的值:最大的长度和数值顶多两类(放在两个box里面)
max_len = 0
num = Counter() # 计算子数组中的种类
# 使用滑动窗口来判定
for right, k in enumerate(fruits):
num[k] += 1
while len(num) > 2:
num[fruits[left]] -= 1 #如果大于2,就左指针fruits[left]减掉,不采摘
if num[fruits[left]] == 0: #如果 fruits[left]在哈希表中的出现次数减少为0,需要将对应的键值从num中移除。
num.pop(fruits[left])
left += 1
max_len = max(max_len, right - left + 1)
return max_len
76.最小覆盖子串
我暂时还理解不了,等二刷的时候再做吧~
59.螺旋矩阵Ⅱ
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解:代码随想录
视频讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili
关键理解转圈的逻辑,运用在二分搜索中提到区间定义
题目:
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
思路:
-
注意旋转的方向:从左至右,从上至下,从右至左,从下至上
-
保持循环不变量原则:左闭右开,每一层设置一个offset量,用于控制余一个位置做开区间。
题解:
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
nums = [[0] * n for _ in range(n)]
startx,starty = 0, 0 # x,y 方向的起始点
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
数组章节总结
-
循环不变量原则
-
双指针思想:快慢指针,对撞指针
-
特殊的双指针思想:滑动窗口思想
-
理解清楚螺旋矩阵的打印方法,注意确定好区间,遵守循环不变量的原则。