155, 88(space complexity needs to be o ( 1 ) o(1) o(1)), 380, 139, 275
168
136(think about how to generalize to further conditions, for example, how to find the only element that appears at n
times, while other elements appear m
times)
162, 1027
918
146,链表必做
贪心:435
二分:33
参考链接:https://wiki.python.org/moin/TimeComplexity
list相关 | 时间复杂度 |
---|---|
转换成set | o ( n ) o(n) o(n) |
in | o ( n ) o(n) o(n) |
len | o ( 1 ) o(1) o(1) |
dict相关 | 时间复杂度 |
---|---|
get | o ( 1 ) o(1) o(1) |
pop(删掉某个key) | o ( 1 ) o(1) o(1) |
len | o ( 1 ) o(1) o(1) |
in | almost certainly o ( 1 ) o(1) o(1), unless some weird inputs to become the worst, o ( n ) o(n) o(n) |
set相关 | 时间复杂度 |
---|---|
in | 平均 o ( 1 ) o(1) o(1),最坏 o ( n ) o(n) o(n) |
max(a), min(a)
,时间复杂度都是o(n)
sort
的时间复杂度是 o ( n l o g n ) o(nlogn) o(nlogn)
too easy, don’t even waste your time
缺失数字系列,思路是空间换时间:268. 缺失数字, 41. 缺失的第一个正数
88. 合并两个有序数组(空间 o ( 1 ) o(1) o(1)值得思考)
easy, useful when you haven’t coding for a long time
977. 有序数组的平方
724. 寻找数组的中心索引
49. 字母异位词分组
1013. 将数组分成和相等的三个部分
medium
290. Word Pattern
448. 找到所有数组中消失的数字(可以修改原数组的元素), 287. 寻找重复数(不可以修改原数组的元素)
11. 盛最多水的容器
17. 电话号码的字母组合
334. 递增的三元子序列
36. 有效的数独(熟悉python特殊用法)、37. Sudoku Solver(follow-up)
274. H-Index( o ( n ) o(n) o(n)做法可以思考下)
380. Insert Delete GetRandom O(1)(值得思考)
54. Spiral Matrix (The devil is in the details!!!)
42. Trapping Rain Water
Boyer-Moore Majority Vote
169, 229. Majority Element II
查找常用字符
区间类
56. 合并区间、57. 插入区间、区间列表的交集
划分字母区间、提莫攻击
最小区间
435. 无重叠区间、用最少数量的箭引爆气球
206. 反转链表、92. 反转链表 II、25. K 个一组翻转链表、两两交换链表中的节点
旋转链表
复制带随机指针的链表
86. Partition List
重排链表
链表中点、删除链表的倒数第N个节点
141. 环形链表(判断链表中是否有环)、142. 环形链表 II(找链表中环的入口)
移除链表元素、移除链表中的重复节点
21. 合并两个有序链表、合并K个排序链表
Merge In Between Linked Lists
725. Split Linked List in Parts
2. 两数相加、两数相加II
146. LRU Cache
3. 无重复字符的最长子串、K 个不同整数的子数组
员工的重要性
单调队列
队列里的元素要么单调递增,要么单调递减
239. 滑动窗口最大值
绝对差不超过限制的最长连续子数组
双端搜索
核心思想:寻找图中某个点到某个点是否存在联通的路径,那么可以从起点和中点一起出发,如果2头有重叠的点,则找到联通的路径了(不知道为啥代码写的不对。。。暂时不管这里了)
树状的结构
建堆时间复杂度分析
结论:建堆的时间复杂度:在n个节点上建堆,时间复杂度是 o ( n ) o(n) o(n)
分析:
看最坏的时间复杂度,假设n个节点,高度为h,则第i层有 2 i − 1 2^{i - 1} 2i−1个节点,计算建堆的时间复杂度,既可以按照一个一个节点插入堆,然后调整节点来考虑,也可以按照所有节点都随机摆好,然后一个一个调整来计算。这里使用第二种情况考虑,最坏情况下,每个节点都需要考虑调整位置:
第h - 1
层的节点有 2 h − 1 2^{h - 1} 2h−1个,需要调整1次,第h - 2
层的节点有 2 h − 2 2^{h - 2} 2h−2个,需要调整2次,以此类推,总共需要调整的次数s
为:
s = 1 ∗ 2 h − 1 + 2 ∗ 2 h − 2 + ⋯ + ( h − 1 ) ∗ 2 1 + h ∗ 2 0 \begin{aligned} s &= 1*2^{h - 1} + 2 * 2^{h - 2} + \dots + (h - 1) * 2^1 + h * 2^0 \\ \end{aligned} s=1∗2h−1+2∗2h−2+⋯+(h−1)∗21+h∗20
求解s
,先求 1 2 s \frac{1}{2}s 21s,有:
1 2 s = 1 ∗ 2 h − 2 + 2 ∗ 2 h − 3 + ⋯ + ( h − 1 ) ∗ 2 0 + 1 2 h \begin{aligned} \frac{1}{2}s &= 1*2^{h - 2} + 2 * 2^{h - 3} + \dots + (h - 1) * 2^0 + \frac{1}{2}h \\ \end{aligned} 21s=1∗2h−2+2∗2h−3+⋯+(h−1)∗20+21h
两者相减,有:
1 2 s = 2 h − 1 + 2 h − 2 + ⋯ + 2 0 − 1 2 h ⇔ s = 2 h + 2 h − 1 + ⋯ + 2 − h = 2 ( 1 − 2 h ) 1 − 2 − h = 2 h + 1 − 2 − h \begin{aligned} \frac{1}{2}s &= 2^{h - 1} + 2^{h - 2} + \dots + 2^0 - \frac{1}{2}h \\ \Leftrightarrow \;\; s &= 2^h + 2^{h - 1} + \dots + 2 - h \\ &= \frac{2(1 - 2^h)}{1 - 2} - h \\ &= 2^{h + 1} - 2 - h \end{aligned} 21s⇔s=2h−1+2h−2+⋯+20−21h=2h+2h−1+⋯+2−h=1−22(1−2h)−h=2h+1−2−h
因为 h = l o g 2 n h = log_2n h=log2n,所以上式继续化简,最终得到:
s = 2 h + 1 − 2 − h = 2 h − 4 − h = 2 l o g 2 n − 4 − h = n − 4 − l o g 2 n s = 2^{h + 1} - 2 - h = 2^h - 4 - h = 2^{log_2n} - 4 - h = n - 4 - log_2n s=2h+1−2−h=2h−4−h=2log2n−4−h=n−4−log2n
所以建堆的复杂度是 o ( n ) o(n) o(n)
堆查找/堆插入/堆删除的时间复杂度分析
结论:假设建成的堆高度为logn
,即堆内有n
个节点,则插入/查找/删除节点的时间复杂度是 o ( log n ) o(\log n) o(logn)
分析:插入的复杂度主要在节点调整上面了,调整1个节点的最坏时间复杂度就是 o ( log n ) o(\log n) o(logn)
从n个数字中找出最小的k个数字
最优的时间复杂度是 o ( n log k ) o(n \log k) o(nlogk)
先对前面k个数建个大根堆,时间复杂度是 o ( k ) o(k) o(k),然后再对剩下的n - k
进行比较:是否比堆顶元素小?如果是,则将当前的数字插入到堆里(插入消耗 o ( l o g k ) o(logk) o(logk)的时间),否则继续向后遍历。所以时间复杂度是 o ( n l o g k ) o(n \; logk) o(nlogk)
215. 数组中的第K个最大元素
2462. Total Cost to Hire K Workers
253. Meeting Rooms II
数据流的中位数
最接近原点的 K 个点
数据流中的第K大元素
前 K 个高频元素
373. Find K Pairs with Smallest Sums
767. 重构字符串
最低加油次数
medium, worth thinking
1871. Jump Game VII
hard, worth thinking
1696. Jump Game VI
630. Course Schedule III
too easy, don’t waste your time
20. 有效的括号
155. Min Stack(感觉挺重要的题目)
最长有效括号
150. 逆波兰表达式求值
删除字符串中的所有相邻重复项 II
394. 字符串解码
基本计算器(只有+, -, ()
)、基本计算器 II(+, -, *, /
,无括号)(内含有括号的代码)
735. Asteroid Collision
单调栈
42、496、901、402、581、238、407、
316. Remove Duplicate Letters
456. 132 Pattern
每日温度
柱状图中最大的矩形
遍历
前序、中序、后序遍历的递归版写法:
# preorder
def preorder(root: TreeNode):
if not root:
return
print(root.val)
preorder(root.left)
preorder(root.right)
# inorder
def inorder(root: TreeNode):
if not root:
return
preorder(root.left)
print(root.val)
preorder(root.right)
# postorder
def postorder(root: TreeNode):
if not root:
return
preorder(root.left)
preorder(root.right)
print(root.val)
树的遍历,用递归的方法非常简单,但是递归的空间复杂度较高。改成非递归的形式,标记当前结点的访问情况,非常好记,也很方便延伸到其他题目。这个方法来自leetcode的一篇题解:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/yan-se-biao-ji-fa-yi-chong-tong-yong-qie-jian-ming/
非递归版写法:
def marked_preorder(root: TreeNode) -> list:
res = []
stack = [(root, 0)]
while stack:
node, status = stack.pop()
if not node:
continue
if status == 0:
stack.append((node.right, 0))
stack.append((node.left, 0))
stack.append((node, 1))
else:
res.append(node.val)
return res
def marked_inorder(root: TreeNode) -> list:
res = []
stack = [(root, 0)]
while stack:
node, status = stack.pop()
if not node:
continue
if status == 0:
stack.append((node.right, 0))
stack.append((node, 1))
stack.append((node.left, 0))
else:
res.append(node.val)
return res
def marked_postorder(root: TreeNode) -> list:
res = []
stack = [(root, 0)]
while stack:
node, status = stack.pop()
if not node:
continue
if status == 0:
stack.append((node, 1))
stack.append((node.right, 0))
stack.append((node.left, 0))
else:
res.append(node.val)
return res
遍历相关题目:
特殊的二叉树
其他
DFS和BFS的时间复杂度均为 o ( V + E ) o(V+E) o(V+E),空间复杂度均为 o ( V ) o(V) o(V)。Note that V V V and E E E are not always equal to n
easy, useful only for rehabilitation
1462. Course Schedule IV
1615. Maximal Network Rank
核心思想:栈,入栈前检查是否符合入栈条件,符合则先标记、再入栈。保证栈内元素都符合入栈条件,且已被标记避免重复访问。
leetcode的题目基本思路都差不多,同行题目难度从左向右递增
1306. 跳跃游戏 III
130. 被围绕的区域
200. 岛屿数量、695. 岛屿的最大面积、463. 岛屿的周长、827. Making A Large Island
不同路径 III
判断二分图
矩阵中的最长递增路径
克隆图
图像渲染
79. 单词搜索(其实是用栈模拟了回溯过程)
冗余连接
1034
核心思想:队列。由于后放入队列的元素,其step
必然更大,所以先出来的元素一定是最短的。
1129. Shortest Path with Alternating Colors、1345. 跳跃游戏 IV、752. 打开转盘锁
Minimum Moves to Reach Target with Rotations
542. 01 Matrix
994,310
864. Shortest Path to Get All Keys
核心思想:每次从入度为0的点开始访问,每访问一个点,就把点的入度-1,如果点的入度被减为0,则把点加入到可访问队列中,直到图中再没有可以访问的点为止
207. 课程表、210. 课程表 II
1340. 跳跃游戏 V
冗余连接 II(超时未ac)
168. Excel Sheet Column Title
14. Longest Common Prefix
判断子序列
字典序的第K小数字
Z 字形变换
外观数列
比较含退格的字符串
重构字符串
执行操作后字典序最小的字符串
字符串+数字
8. 字符串转换为整数(atoi)(可用有限自动机解)、7. 整数反转、有效数字
匹配
模式匹配、正则表达式匹配
dp相关
可见dp中的字符串类
子串相关的问题
139. 单词拆分(很值得做)、单词拆分II、连接词(WA)
其他类
第N个数字、学生出勤记录 I
前缀树在大量字符串查表过滤的时候很有用
实现 Trie (前缀树)
Remove Sub-Folders from the Filesystem
回文串就是中心对称的字符串
回文数、验证回文串、验证回文字符串 Ⅱ(可删除1个字符)、回文链表
5. 最长回文子串、最长回文串(利用给出的字符串构造最长回文串)、最长回文子序列、由子序列构造的最长回文串的长度、最短回文串
分割回文串、分割回文串II
回文子串(统计回文子串的个数)
回文对
冗余连接
带阈值的图连通性
模式匹配
贪心的题目,一般需要先对数据进行排序,然后从最小的开始。
有时候可能需要证明为什么局部最优解是全局最优解。最方便的方法是反证法:假设不用当前这个顺序,那么新的顺序得到的值,比按照贪心的顺序得到的值更小,更无法满足要求。
分发饼干
135. 分发糖果(贪心+分治)
2366. Minimum Replacements to Sort the Array
55. 跳跃游戏、45. 跳跃游戏 II、1326. Minimum Number of Taps to Open to Water a Garden(Variant of jump game II: self-built jump list, and there might be no valid solution)
1647. Minimum Deletions to Make Character Frequencies Unique
1024
用最少数量的箭引爆气球
任务调度器
区间系列
Interval scheduling: earliest finish first
435. 无重叠区间、646. Maximum Length of Pair Chain
Interval partitioning: earliest start first
2406. Divide Intervals Into Minimum Number of Groups
子序列系列
334. 递增的三元子序列(结合LIS)
300. 最长上升子序列(贪心+二分,必做)、354. Russian Doll Envelopes
630. Course Schedule III
分发糖果(贪心+分治)
core function is f(already_done, remaining_variables)
. The problems could be sorted into these categories:
Category | Time Complexity | Space Complexity |
---|---|---|
subset | o ( n ∗ 2 n ) o(n*2^n) o(n∗2n) For each element, we decide whether to choose (1) or not (0), so there will be 2 n 2^n 2n states, every state takes o ( n ) o(n) o(n). |
o ( n ) o(n) o(n) |
combination | o ( C n k ∗ k ) o(C_n^k*k) o(Cnk∗k) There will be C n k C_n^k Cnk combinations, and every combination takes k k k time. This is a special case in subset, so the worst case won’t exceed n ∗ 2 n n*2^n n∗2n |
o ( n ) o(n) o(n) |
permutation | o ( n ! ∗ n ) o(n! * n) o(n!∗n) There will be n ! n! n! permutations, and each permutation takes o ( n ) o(n) o(n). |
o ( n ) o(n) o(n) |
N-queen | ( n ! ) (n!) (n!) | o ( n ) o(n) o(n) |
sudoku | o ( 9 n ) o(9^n) o(9n) | o ( n 2 ) o(n^2) o(n2) |
17. 电话号码的字母组合
105. Construct Binary Tree from Preorder and Inorder Traversal、106. Construct Binary Tree from Inorder and Postorder Traversal
22. 括号生成
93. 复原IP地址
39. 组合总和、40. 组合总和 II、216. 组合总和 III、377. 组合总和 Ⅳ(follow-up)
77. 组合
46. Permutations、47. Permutations II
78. Subsets
51. N皇后、52. N皇后 II
37. Sudoku Solver
79. 单词搜索
1601. Maximum Number of Achievable Transfer Requests
1751. Maximum Number of Events That Can Be Attended II
735. Asteroid Collision
2369. Check if There is a Valid Partition For The Array
403. Frog Jump
486. 预测赢家
划分为k个相等的子集、火柴拼正方形(思路和前面完全一样,但是更简单)
90, 47, 31, 60
不同的二叉搜索树 II
最大二叉树(递归)
戳气球(超时)、移除盒子(超时)
外观数列
24 点游戏
60. 第k个排列
python自带的sort用了timesort,平均时间复杂度是 o ( n l o g n ) o(nlogn) o(nlogn)
练习各类排序的题:排序数组
摆动排序 II
常见的排序比较
排序方法 | 主要思想 | 时间复杂度 | 稳定性 |
---|---|---|---|
直接插入排序/insertion sort | 无序的数字向有序列表中插 | o ( n 2 ) o(n^2) o(n2) | 稳定 |
折半插入排序 | 无序的数字二分法向有序列表中插 | o ( n 2 ) o(n^2) o(n2) | 稳定 |
希尔排序/shell sort | 把数组分成长度为g 的子数组,每次对每个子数组相同位置的数字进行排序(如把每个子数组的第1个数字抽出来排序),循环直到g=1 g 需要选择终值为1的递减序列 |
g n + 1 = 3 g n + 1 g_{n+1}=3g_n+1 gn+1=3gn+1时,复杂度稳定在 o ( n 1.25 ) o(n^{1.25}) o(n1.25) | 不稳定 |
冒泡排序/bubble sort | 每次遍历都找出来最大/最小的元素 | o ( n 2 ) o(n^2) o(n2) | 稳定 |
选择排序/selection sort | 每次遍历找出最小/最大元素,和当前值交换位置 | o ( n 2 ) o(n^2) o(n2) | 不稳定 |
快速排序 | 每次排序都排好某个数,让比这个数小的数都在这个数左边,比这个数大的数都在这个数右边,实现可见这里 | o ( n l o g n ) o(nlogn) o(nlogn) | 不稳定 |
堆排序 | o ( n l o g n ) o(nlogn) o(nlogn) | 不稳定 | |
归并排序 | 排左边,排右边,再合并有序数组 | o ( n l o g n ) o(nlogn) o(nlogn) | 稳定 |
应用场景及题目
排序方法 | 常用场景 | 相关题目 |
---|---|---|
直接插入排序 | ||
折半插入排序 | ||
希尔排序 | ||
冒泡排序 | 找到最大值 | |
快速排序 | 所有排序题目,建议首选 | 215. 数组中的第K个最大元素 |
堆排序 | ||
归并排序 | 链表排序 | 合并两个有序数组、合并两个有序链表、合并K个排序链表 有序矩阵中第K小的元素 排序链表、翻转对 |
快排实现
递归版,核心思想是分治法
时间复杂度:平均o(nlogn),不稳定
思想:每次排好1个数,在这个数前面的都是比它小的,在它后面的都是比它大的
优化点:如果每次都选择中间的数字作为pivot
,对于已经排序好的数组,最坏情况复杂度会达到 o ( n 2 ) o(n^2) o(n2),所以选择pivot
非常重要,一般最好随机选择。同时记录与pivot
相同的数字,以降低复杂度
def quick_sort(nums: list) -> list:
if len(nums) < 2:
return nums
pivot = random.choice(nums)
left = [x for x in nums if x < pivot]
right = [x for x in nums if x > pivot]
mid = [x for x in nums if x == pivot]
return quick_sort(left) + mid + quick_sort(right)
非递归版,核心思想是用栈来模拟递归
def partition(nums: list, left: int, right: int) -> tuple:
"""
put pivot to the right place
Returns:
left_index, right_index for pivot
"""
pivot = nums[random.choice(range(left, right + 1))]
left_nums = [x for x in nums[left:right + 1] if x < pivot]
right_nums = [x for x in nums[left: right + 1] if x > pivot]
mid_nums = [x for x in nums[left: right + 1] if x == pivot]
sorted_nums = left_nums + mid_nums + right_nums
j = 0
for i in range(left, right + 1):
nums[i] = sorted_nums[j]
j += 1
return left + len(left_nums), left + len(left_nums) + len(mid_nums) - 1
def sortArray(nums: List[int]) -> List[int]:
left, right = 0, len(nums) - 1
stack = [(left, right)]
while stack:
left, right = stack.pop()
new_left, new_right = partition(nums, left, right)
if new_left > left:
stack.append((left, new_left - 1))
if new_right < right:
stack.append((new_right + 1, right))
return nums
三色旗问题
一个很有意思的排序问题,核心思想是保存最小的数字指针和最大的数字指针。题目是75. 颜色分类
二分查找
核心思想:丢弃不符合要求的一半数据,在剩下的一半中继续寻找
关键点:
left, right
的选取:left, right
代表要搜索的空间范围,正常来说,left = 0, right = len(nums)-1
,但比如是插入的题,那么right = len(nums)
while left < right
mid
。需要注意,实际上最终的结果应该是mid
,但因为大部分情况下退出都是因为left=right
,所以直接返回left
或者right
即可。但如果是插入题,初始化right = len(nums)
,同时mid
取值选了右边界,可能会导致超出取数的索引,此时需要额外处理mid
的取值方式,要和boundary shrink的方式匹配# mid取左边界,那么下一步更新的时候,就要exclude旧left
mid = (left + right) // 2
if target > nums[mid]:
left = mid + 1
else:
right = mid
# mid取右边界,那么下一步更新exclude right
mid = (left + right + 1) // 2
if target < nums[mid]:
right = mid - 1
else:
left = mid
注意在exclude时,还可以进一步考虑,当已经找到了后,要不要把这个位置exclude掉。如果exclude掉,那么返回的索引,是插入的最右索引(如bisect.bisect_right
),此时需要判断nums[loc-1]
是否与target
相同。写法上:
mid = (left + right) // 2
if target < nums[mid]:
right = mid
else:
left = mid + 1
感觉其实mid
取左边界还是右边界无所谓的,重点是更新的时候,丢弃哪一部分。可以直接把题目分成2种,找到最左边界,找到最右边界。
mid = left
,丢弃左边小于target
的部分。mid = right
,丢弃右边大于target
的部分。总结题目类型。假设nums
里可以有重复值,按照如果查找到,则返回索引,如果查不到,则返回插入的位置,那么可以分为,返回最左边的索引、返回最右边的索引
因为是插入,所以right=len(nums)
。以返回最左边索引为例,令mid
更新为左边界,按照exclude old left来更新,有:
def f_left(nums: list, target: int) -> int:
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if target > nums[mid]:
left = mid + 1
else:
right = mid
return left
类似地,以返回最右索引为例,需要额外注意,因为初始化时right=len(nums)
,导致插入的数字如果是最右边,那么在循环内会导致list
取数出错,所以判断一下如果超过边界了就跳出循环。同时注意最终返回值应该是更新后的mid
def f_right(nums: list, target: int) -> int:
left, right = 0, len(nums)
while left < right:
mid = (left + right + 1) // 2
if mid >= len(nums):
break
if target < nums[mid]:
right = mid - 1
else:
left = mid
return (left + right + 1) // 2
python3内置了二分查找,见python3刷题技巧的bisect章节
值得一做:275. H-Index II,2616. Minimize the Maximum Difference of Pairs
852. Peak Index in a Mountain Array
1870. Minimum Speed to Arrive on Time
2251. Number of Flowers in Full Bloom
410,774,875,1011,1231,1283,1482,1539,1802,2226,2560,2616
实现某种运算
两数相除
50. Pow(x, n)
二分插入
计算右侧小于当前元素的个数
分类参考了这篇博客:https://blog.csdn.net/cc_again/article/details/25866971
70. Climbing Stairs
53. 最大子序和、918. Maximum Sum Circular Subarray
542. 01 Matrix
2707. Extra Characters in a String
486. 预测赢家
Maximum Number of Consecutive Values You Can Make
杨辉三角、杨辉三角 II
不相交的线
62. 不同路径、63. 不同路径 II
地下城游戏
三角形最小路径和
不同的二叉搜索树
学生出勤记录 II
目标和
最大平均值和的分组、分割数组的最大值
整数拆分、剪绳子
764, 221, 85
1638
2328. Number of Increasing Paths in a Grid
最小路径和
198. 打家劫舍(简单递推)、213. 打家劫舍 II(2遍递推)、337. 打家劫舍 III(树形dp)
5545/1646-无矛盾的最佳球队(需要自己构造数组辅助dp)
1027. Longest Arithmetic Subsequence
300. 最长上升子序列、354. Russian Doll Envelopes
乘积最大子数组
646, 673, 712
递增子序列
最长公共子序列、最长回文子序列
最长重复子数组
124. 二叉树中的最大路径和、二叉树的坡度
树中距离之和
weighted interval scheduling
1235. Maximum Profit in Job Scheduling、2830. Maximize the Profit as the Salesman
戳气球(递归超时)、移除盒子(递归超时)
这类问题,是普通的dp可以解决,但是时间复杂度比较高,需要结合其他数据结构做优化
1696. Jump Game VI
1871. Jump Game VII
1218. Longest Arithmetic Subsequence of Given Difference
编辑距离、两个字符串的删除操作(编辑距离变种之只有删除操作)、712. 两个字符串的最小ASCII删除和(编辑距离变种之只有删除操作)
通配符匹配、正则表达式匹配
97. 交错字符串
0-1背包
分割等和子集、474、494
完全背包
322、518
面试题 08.11. 硬币
1、组合问题:
377. 组合总和 Ⅳ
494. 目标和
518. Coin Change II
2、True、False问题:
139. 单词拆分
416. 分割等和子集
3、最大最小问题:
474. 一和零
322. 零钱兑换
参考题解:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/yi-ge-tong-yong-fang-fa-tuan-mie-6-dao-gu-piao-wen/
dp本质是状态转移,所以可以先穷举所有状态,然后再用dp压缩中间过程
121. 买卖股票的最佳时机(只能进行一次交易)
122. 买卖股票的最佳时机 II(无限次交易)
123. 买卖股票的最佳时机 III(最多买卖2次)
188. 买卖股票的最佳时机 IV(最多买卖k次)
714. 买卖股票的最佳时机含手续费(无限次交易、买卖增加手续费)
309. 最佳买卖股票时机含冷冻期(无限次交易、第二次买入和上一次卖出需要间隔1天)
实际上,上述所有题目,都有一个前提,股票只能卖掉再买,也就是当前手中只能持有1个股票。更进一步地,还可以讨论当前手中最多能持有k个股票。再进一步地,可以有不同的股票,不同的价格,手中每种股票还能再持有k个……
前缀和是数组前i项之和,包括i项
. | 定义式 | 递推式 |
---|---|---|
一维 | b [ i ] = ∑ k = 0 i a [ k ] b[i] = \sum_{k = 0}^ia[k] b[i]=∑k=0ia[k] | b [ i ] = a [ 0 ] + a [ 1 ] + ⋯ + a [ i ] b[i] = a[0] + a[1] + \dots + a[i] b[i]=a[0]+a[1]+⋯+a[i] |
二维 | b [ i ] [ j ] = ∑ x = 0 i ∑ y = 0 j a [ x ] [ y ] b[i][j] = \sum_{x = 0}^i\sum_{y = 0}^j a[x][y] b[i][j]=∑x=0i∑y=0ja[x][y] | b [ i ] [ j ] = b [ i − 1 ] [ j ] + b [ i ] [ j − 1 ] − b [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i-1][j-1] + a[i][j] b[i][j]=b[i−1][j]+b[i][j−1]−b[i−1][j−1]+a[i][j] |
前缀和常见的题目,一般是求满足xx条件的子数组,注意子数组/subarray和子序列/subsequence之前的区别。
子数组/subarray: 连续的
子序列/subsequence: 可以不连续
前缀和的核心点在于,确定好相减这个操作的物理意义,尤其是被减掉的那个子数组。通常情况下,被减掉的子数组没有特殊要求,如560. 和为K的子数组等。但也有特定要求的子数组,如2488. Count Subarrays With Median K,就要求被减掉的数组是在本题中的k
左边以保证中位数
一维前缀和
和为K的子数组(基础题目)、路径总和 III(基础题+树的遍历,树的路径是数组)
和可被k整除的子数组(取模)、连续的子数组和(倍数,考虑0的取模)
每个元音包含偶数次的最长子字符串(奇偶性,用异或的值来作为键)
2488. Count Subarrays With Median K
二维前缀和
矩阵区域和(基础)
238. Product of Array Except Self
hard, worth thinking
1871. Jump Game VII
求二进制中1的个数(x & x - 1, eliminate the rightest 1)
389. Find the Difference
136. 只出现一次的数字(异或解法、通用解法)、137. 只出现一次的数字 II(数学解法)、260. Single Number III
191. Number of 1 Bits、201. 数字范围按位与
汉明距离
1125. Smallest Sufficient Team
338. Counting Bits
第N个数字、快乐数
172. Factorial Trailing Zeroes
2811. Check if it is Possible to Split Array
459. Repeated Substring Pattern
最基础、最简单的就是1. 两数之和,核心思想:用哈希表查询target - num
接下来以此为基础的题目,在数字上变形的,有:
在输入形式上变形的,有:
核心思想:从左向右加,每次结果乘10
题目:字符串相乘
基本计算器(只有+, -, ()
)、基本计算器 II(+, -, *, /
,无括号)(内含有括号的代码)
滑动窗口适用于这类题目:
209. 长度最小的子数组、乘积小于K的子数组
K 个不同整数的子数组
1493. Longest Subarray of 1‘s After Deleting One Element、2024. Maximize the Confusion of an Exam
3. 无重复字符的最长子串
2090. K Radius Subarray Averages
239
438. Find All Anagrams in a String
下一个排列
感觉这类题目出得好的很有难度,出得一般的写起来怪怪的。。。
设计推特
Design Authentication Manager