LeetCode977.有序数组的平方
题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/description/
视频讲解: https://www.bilibili.com/video/BV1QB4y1D7ep
掌握程度:★★☆
这道题中的双指针是左右指针向中间靠拢的形式。
循环条件可以通过左右指针位置判断,写成 while left <= right;也可以通过结果是否完全填充判断,写成 for index in range(len(nums)-1, -1, -1).
比较大小的最直观方式是 比较两个平方的大小,nums[left] × nums[left] 和 nums[right] × nums[right] 的大小。为了简便计算,也可以比较 -nums[left] 和 nums[right] 的大小。
解决办法
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
l = len(nums)
left, right = 0, l - 1
res = [0] * l
for index in range(l-1, -1, -1):
left_num, right_num = nums[left], nums[right]
if -left_num <= right_num:
res[index] = nums[right] * nums[right]
right -= 1
else:
res[index] = nums[left] * nums[left]
left += 1
return res
LeetCode209. 长度最小的子数组
题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/
视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE
掌握程度:★☆☆
这道题的思路是滑动窗口。
【复杂做法,用于记录,建议忽略】
我拿到这道题最初的思路是快慢指针法:
- 当子数组之和大于target时,需要减去一个数,slow += 1;
- 当字数组之和小于target时,需要加上一个数,fast += 1;
- 当子数组之和等于target时,未找到最小子数组,应先减后加。
循环的停止条件:
- .fast += 1 导致fast超出边界,停止
- slow += 1 导致slow超出边界,停止
需要注意的是:
- fast += 1 写在 sum_nums += nums[fast] 之前,如果将循环条件写为 while fast < len(nums),会出现超出边界的错误,因此只能在循环内写 if fast == len(nums)-1: break
- slow += 1写在 sum_nums -= nums[slow] 之后,可以将循环条件写为 while slow < len(nums)
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
slow, fast = 0, 0
min_len = float('inf')
sum_nums = nums[0]
while slow < len(nums):
print(slow,fast,sum_nums)
if sum_nums < target:
if fast <= len(nums)-2:
fast += 1
sum_nums += nums[fast]
else:
break
elif sum_nums > target:
min_len = min(min_len, fast - slow + 1)
sum_nums -= nums[slow]
slow += 1
else:
min_len = min(min_len, fast - slow + 1)
sum_nums -= nums[slow]
slow += 1
if fast <= len(nums)-2:
fast += 1
sum_nums += nums[fast]
else:
break
return 0 if min_len == float('inf') else min_len
滑动窗口思路
这里的fast是持续增加的,不需要对fast的边界做分析,只要分析slow在什么条件下移动即可。
按照上一节的思路,slow只要移动到使得 sum_nums < target 就可以。因此条件为 while sum_nums >= target.
在这里,需要分析一下 sum_nums == target 时,slow是否可以继续增加。slow += 1 在 sum_nums -= nums[slow] 之后,所以 slow == fast+1 时,sum_nums == 0,进入for的下一次循环, fast += 1,不会出现区间无定义的情况。
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
slow = 0
min_len = float('inf')
sum_nums = 0
for fast in range(len(nums)):
sum_nums += nums[fast]
while sum_nums >= target:
min_len = min(min_len, fast-slow+1)
sum_nums -= nums[slow]
slow += 1
return 0 if min_len == float('inf') else min_len
LeetCode904. 水果成篮
题目链接:https://leetcode.cn/problems/fruit-into-baskets/description/
掌握程度:★★☆
仍然是滑动窗口,这道题的关键是如何实时记录水果种类。
slow移动的条件也是水果种类超出限制。
暴力in
先给出我的超时做法,用 数字 in 数组 的方式计算水果是否是新种类。
class Solution(object):
def totalFruit(self, fruits):
"""
:type fruits: List[int]
:rtype: int
"""
slow = 0
max_fruits = 0
kind = 0
for fast in range(len(fruits)):
if fruits[fast] not in fruits[slow:fast]:
kind += 1
while kind > 2:
if not fruits[slow] in fruits[slow+1 : fast+1]:
kind -= 1
slow += 1
max_fruits = max(max_fruits, fast-slow+1)
return max_fruits
哈希表
相比之下,用哈希表记录数量应该更快吧。
class Solution(object):
def totalFruit(self, fruits):
"""
:type fruits: List[int]
:rtype: int
"""
slow = 0
max_fruits = 0
fruits_count = collections.defaultdict(int)
for fast in range(len(fruits)):
fruits_count[fruits[fast]] += 1
while len(fruits_count) > 2:
fruits_slow = fruits[slow]
fruits_count[fruits_slow] -= 1
if fruits_count[fruits_slow] == 0:
fruits_count.pop(fruits_slow)
slow += 1
max_fruits = max(max_fruits, fast-slow+1)
return max_fruits
通过了
这里的 fruits_count = collections.defaultdict(int) 指字典的默认值为0,导入方式是 import collections.
Counter函数
官方解答上还在for中加了enumerate
class Solution(object):
def totalFruit(self, fruits):
"""
:type fruits: List[int]
:rtype: int
"""
slow = 0
max_fruits = 0
fruits_count = Counter()
for fast, fruites_fast in enumerate(fruits):
fruits_count[fruites_fast] += 1
while len(fruits_count) > 2:
fruits_slow = fruits[slow]
fruits_count[fruits_slow] -= 1
if fruits_count[fruits_slow] == 0:
fruits_count.pop(fruits_slow)
slow += 1
max_fruits = max(max_fruits, fast-slow+1)
return max_fruits
Counter的导入方式 from collections import Counter.
LeetCode 76. 最小覆盖子串
题目链接:https://leetcode.cn/problems/minimum-window-substring/description/
掌握程度:★★☆
仍然滑动窗口,难点在于确定子串是否覆盖给定字符串
两个哈希表:
上一道题中,对不同数字的统计用的是哈希表的方法,这里我们也可以这么做。按照上一题的思路,我们对出现的字母做哈希表,比较特定字母的数量是否>=给定字符串字母数量。
一个哈希表:
这么做还是复杂了,有一个更简单的方法:将给定字符串字母数量设置为哈希表need,在子串中遇到该字母时,只需要对need做加减即可。(我愿称之为 残差哈希表
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
need = collections.defaultdict(int)
res = ""
min_len = float('inf')
slow = 0
for a in t:
need[a] += 1
for fast, s_fast in enumerate(s):
need[s_fast] -= 1
while all(v <= 0 for v in need.values()):
if len(s[slow: fast+1]) < min_len:
res = s[slow: fast+1]
min_len = len(res)
s_slow = s[slow]
need[s_slow] += 1
slow += 1
return res
slow更新的循环条件还可以优化,以 仍需更新的字母个数 为标志。
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
need = collections.defaultdict(int)
need_len = len(t)
res = ""
min_len = float('inf')
slow = 0
for a in t:
need[a] += 1
for fast, s_fast in enumerate(s):
if need[s_fast] > 0 :
need_len -= 1
need[s_fast] -= 1
while need_len == 0:
if len(s[slow: fast+1]) < min_len:
res = s[slow: fast+1]
min_len = len(res)
s_slow = s[slow]
need[s_slow] += 1
if need[s_slow] > 0:
need_len += 1
slow += 1
return res
LeetCode59. 螺旋矩阵 II
题目链接:https://leetcode.cn/problems/spiral-matrix-ii/
视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/
掌握程度:★★★
这道题的重点在于边界的控制条件。
- 矩阵旋转填写数字,一共有四种方向,把他们分别记为0, 1, 2, 3,分别讨论;
- 该方向的填写在什么时候结束?一是碰到边界,二是碰到已填写过的数字。边界很好判断,无非是坐标为0或n-1;已填写的数字如何判断,我们可以把矩阵初始化为一个零矩阵,如果数字为0,代表未填写过,那么判断下一个需要填写的位置是否为0就可以了;
- 最终循环何时结束?我们需要填写 [1, n*n] 的数字,只要把它作为循环条件就可以了。
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
direct = 0 # 0,1,2,3 四个方向
matrix = [[0]*n for _ in range(n)]
i, j = 0, 0
for num in range(1, n*n+1):
matrix[i][j] = num
if direct == 0:
if j == n-1 or matrix[i][j+1] != 0:
i += 1
direct = 1
else:
j += 1
elif direct == 1:
if i == n-1 or matrix[i+1][j] != 0:
j -= 1
direct = 2
else:
i += 1
elif direct == 2:
if j == 0 or matrix[i][j-1] != 0:
i -= 1
direct = 3
else:
j -= 1
elif direct == 3:
if i == 0 or matrix[i-1][j] != 0:
j += 1
direct = 0
else:
i -= 1
return matrix
这里需要注意的是:
matrix = [[0]*n for _ in range(n)] 不能写成 matrix = [[0]*n]*n
因为 [[0]*n]*n 表示指向同一位置的 [0]*n] 复制n次,改写任意一行的某个数字,都会导致其他行相同位置的数字同步变动。
还有一种方法是:直接设置边界,在边界内循环。
随着方向的改变,边界会向内压缩。
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
left, right, top, bottle = 0, n-1, 0, n-1
matrix = [[0]*n for _ in range(n)]
num = 1
tar = n*n
while num <= tar:
for i in range(left, right+1):
matrix[top][i] = num
num += 1
top += 1
for i in range(top, bottle+1):
matrix[i][right] = num
num += 1
right -= 1
for i in range(right, left-1, -1):
matrix[bottle][i] = num
num += 1
bottle -= 1
for i in range(bottle, top-1, -1):
matrix[i][left] = num
num += 1
left += 1
return matrix
LeetCode 54. 螺旋矩阵
题目链接:https://leetcode.cn/problems/spiral-matrix/description/
掌握程度:★★☆
这道题就不能用上道题解法一的思路了,因为没办法判断已输出的数字位置。
根据解法二的思路:
class Solution(object):
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
left, right, top, bottle = 0, len(matrix[0])-1, 0, len(matrix)-1
res = []
while True:
for i in range(left, right+1):
res.append(matrix[top][i])
top += 1
if top > bottle:
break
for i in range(top, bottle+1):
res.append(matrix[i][right])
right -= 1
if left > right:
break
for i in range(right, left-1, -1):
res.append(matrix[bottle][i])
bottle -= 1
if top > bottle:
break
for i in range(bottle, top-1, -1):
res.append(matrix[i][left])
left += 1
if left > right:
break
return res
退出循环的条件不能在while中写,而只能在每次for循环之后写,因为不是每次都能正好转完四个方向才退出循环。
今天就到这里啦,拜拜┏(^0^)┛