*977.有序数组的平方
题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/
文章讲解:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html
视频讲解: https://www.bilibili.com/video/BV1QB4y1D7ep
- 考点
- 双指针
- 排序算法(实际上没有尝试这种思路,但是发现排序算法的实现遗忘了,具体可见我的另一篇文章)
- 我的思路
- 首先当前数组逐元素平方
- 之后利用排序算法进行排序(实际操作发现不会写排序算法了,后续要写一下)
- 视频讲解关键点总结
- 使用双指针法,具体思路是,带负数的升序数组,其平方后两边元素大,中间元素小,因此可以利用双指针从两边向中间循环读取并赋值给新的升序数组
- 我的思路的问题
- 排序算法要会实现
- 代码书写问题
- 列表的初始化时,直接用
new_list = initial_list
的方式,这将导致new_list实际上仅仅是initial_list的引用,因此对新列表的操作将同步修改原列表(有关变量的赋值规则见我的另一篇文章)
- 列表的初始化时,直接用
- 可执行代码
class Solution(object):
def sortedSquares(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
left = 0
right = len(nums) - 1
new_list = [0] * len(nums)
i = 0
while left <= right:
i -= 1
if nums[left] ** 2 > nums[right] ** 2:
new_list[i] = nums[left] ** 2
left += 1
else:
new_list[i] = nums[right] ** 2
right -= 1
return new_list
209.长度最小的子数组
题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/
文章讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html
视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE
- 考点
- 滑动窗口
- 我的思路
- 暴力解法
- 两层循环,两个指针,外层循环负责区间的起始值,内层循环负责区间的终止值,之后枚举出从每个起始值开始的最短子数组,之后将结果排序并取最小值返回
- 视频讲解关键点总结
- 想要降低时间复杂度,就要减少进入循环的次数
- 交换内外层循环的对象,使用滑动窗口法,减少进入循环的次数
- 外层for循环负责移动区间的终止位置
- 内层循环使用while,判断条件为区间和是否满足要求,若满足,才进入该循环,此时终止位置不动,循环移动初始位置,直到区间和不满足要求
- 在此过程中用一个常数记录最小子数组的长度
- 我的思路的问题
- 代码逻辑使用的暴力解法,也就是正向遍历的思路,因此内外层两个循环每次均执行,严重增加了算法的时间复杂度
- 代码书写问题
- 对于特殊情况的考虑没有到位,比如若未实现目标返回0就遗漏了
- 可执行代码
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
sum = 0
left = 0
min_ = float('inf')
for right in range(len(nums)):
sum += nums[right]
while sum >= target:
min_ = min(min_, right - left + 1)
sum -= nums[left]
left += 1
return min_ if min_ != float('inf') else 0
*3. 无重复字符的最长子串
- 题干
- 思路
- 暴力解法:两层循环分别遍历字符串的开始和结束位置,其中内循环如果发现了当前元素重复,则直接break掉内层循环。这种暴力解法能够压线通过力扣
- 滑动窗口法:观察暴力解法中的规律,当结束位置的字符发生了重复时,能够保证结束字符前一个位置到开始字符之间的字符串没有发生重复,因此,可以不改变结束字符的位置,只移动开始字符;此时仍能保证开始字符到结束字符前一个位置的字符串无重复,也就减少了结束字符的遍历次数,时间复杂度下降到O(n)
- 代码(滑动窗口法)
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
length = 1
right = 1
for i in range(len(s) - 1):
while right < len(s):
if s[right] in s[i:right]:
break
length = max(right - i + 1, length)
right += 1
if right == len(s):
break
return length
*438. 找到字符串中所有字母异位词
- 题干
- 思路
- 暴力解法:外层迭代子串的起始位置,内层判断子串是否是p的异位词
- 优化解法:由于上述过程时间复杂度为O(n * m),(n代表s长度,m代表p长度),而经过分析,内层判断异位词的过程可以通过利用滑动过程中大部分子串字符重复的特点来优化,即降低时间复杂度中的m
- 使用字典存储p中字符出现的次数
- 先遍历s的开头和p是否是异位词,并对应修改字典里字符的次数
- 如果字典里字符次数均为0,说明一一对应上了,则是异位词
- 从m + 1的位置继续迭代子串的起始位置
- 此时每轮迭代只改变子串第一个字符和最后一个字符对应的字典位置(如果字符在字典的key里的话)
- 每轮迭代结束后判断字典里字符次数是否均为0以判断是否是异位词
- 经过上述操作,就把时间复杂度从O(n * m)变成了最大为O(n * 26),这是因为判断异位词的过程不再需要遍历p里的所有字符了,而是只需要遍历p中出现过的英文字母的次数,而英文字母最多有26个
- 代码
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
if len(p) > len(s):
return []
dic = {}
for p_ in p:
if p_ in dic:
dic[p_] += 1
else:
dic[p_] = 1
result = []
for i in range(len(p)):
if s[i] in dic:
dic[s[i]] -= 1
flag = True
for key in dic.keys():
if dic[key] != 0:
flag = False
break
if flag:
result.append(0)
for i in range(len(p), len(s)):
flag = True
if s[i - len(p)] in dic:
dic[s[i - len(p)]] += 1
if s[i] in dic:
dic[s[i]] -= 1
for key in dic.keys():
if dic[key] != 0:
flag = False
break
if flag:
result.append(i - len(p) + 1)
return result
59.螺旋矩阵II
题目链接:https://leetcode.cn/problems/spiral-matrix-ii/
文章讲解:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html
视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/
- 考点
- 边界条件判断
- 转圈的逻辑
- 我的思路
- 没想到好的解决思路
- 视频讲解关键点总结
- 进了视频发现,其实就是一个转圈的思路,只要确定好圈数和每圈里面各条边的循环条件就可以了
- 重点不在于考察一些fancy的算法,更在于考验逻辑的缜密性
- 首先确定螺旋的圈数(螺旋的圈数等于输入值整除2)
- 之后确定每一圈里面各条边的边界条件(左闭右开)(并且每圈的时候左右边界都会变,所以边界条件要设置为变量)
- 再每条边循环赋值
- 再进行循环,每圈进行操作
- 如果初始圈数有额外的中心点,进行中心点赋值(发生在输入值为奇数时)
- 我的思路的问题
- 太想要设计一个fancy的算法了
- 代码书写问题
- 多维数组的定义问题,应使用生成器避免浅拷贝带来的多个维度共用同一内存,具体如何定义详见我的另一篇文章
- 可执行代码
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
matrix = [[0 for _ in range(n)] for _ in range(n)]
start_x, start_y, end_x, end_y = 0, 0, n - 1, n - 1
i, j = 0, 0
count = 1
circle = n // 2
while circle:
while j < end_x:
matrix[i][j] = count
count += 1
j += 1
while i < end_y:
matrix[i][j] = count
count += 1
i += 1
while j > start_x:
matrix[i][j] = count
count += 1
j -= 1
while i > start_y:
matrix[i][j] = count
count += 1
i -= 1
i += 1
j += 1
start_x += 1
start_y += 1
end_x -= 1
end_y -= 1
circle -= 1
if n % 2 == 1:
matrix[i][j] = count
return matrix
54. 螺旋矩阵
- 题干
- 思路
- 本题为模拟题,并没有太fancy的思路,重点在于转圈的逻辑和边界条件的控制
- 第一步,确定螺旋的圈数(由于题目中不是正方形,所以通过总结规律可发现螺旋的圈数是由短边的长度决定的)
- 第二步,对每一圈进行遍历,并确定每一圈的起始点均为该圈的左上角点
- 第三步,每一圈中进行右、下、左、上循环记录参数值,需要注意每个循环结束后的边界条件
- 第四步,对于最后一圈,由于其有时候并不是一个整圈,因此通过该圈剩余未记录点的个数来决定最后一圈的循环到什么时候结束
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
result = []
width = len(matrix[0])
height = len(matrix)
mini = min(width, height)
round_num = mini // 2 if mini % 2 == 0 else (mini // 2) + 1
for i in range(round_num):
if i != round_num - 1:
x, y = i, i
while y < width - i:
result.append(matrix[x][y])
y += 1
y -= 1
x += 1
while x < height - i:
result.append(matrix[x][y])
x += 1
x -= 1
y -= 1
while y >= i:
result.append(matrix[x][y])
y -= 1
y += 1
x -= 1
while x >= i + 1:
result.append(matrix[x][y])
x -= 1
else:
left_num = len(matrix) * len(matrix[0]) - len(result)
x, y = i, i
while y < width - i and left_num != 0:
result.append(matrix[x][y])
left_num -= 1
y += 1
y -= 1
x += 1
while x < height - i and left_num != 0:
result.append(matrix[x][y])
left_num -= 1
x += 1
x -= 1
y -= 1
while y >= i and left_num != 0:
result.append(matrix[x][y])
left_num -= 1
y -= 1
y += 1
x -= 1
while x >= i + 1 and left_num != 0:
result.append(matrix[x][y])
left_num -= 1
x -= 1
return result