刷题系列总结


一个刷题的记录总结。

2024/02/05 update: only keep those worth redoing, I will use strike to mark out those similar but too easy questions.

觉得很重要,很值得做的题目

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

1802

python中常见操作的时间复杂度

参考链接: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)
inalmost 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)

数据结构

数组/hash表

88. 合并两个有序数组(空间 o ( 1 ) o(1) o(1)值得思考)

easy, useful when you haven’t coding for a long time
977. 有序数组的平方
724. 寻找数组的中心索引
1013. 将数组分成和相等的三个部分

290. Word Pattern
448. 找到所有数组中消失的数字(可以修改原数组的元素), 287. 寻找重复数(不可以修改原数组的元素)
11. 盛最多水的容器
17. 电话号码的字母组合
334. 递增的三元子序列
36. 有效的数独(熟悉python特殊用法)、37. Sudoku Solver(follow-up)

1877. Minimize Maximum Pair Sum in Array
2938. Separate Black and White Balls

274. H-Index o ( n ) o(n) o(n)做法可以思考下)

380. Insert Delete GetRandom O(1)(值得思考)

54. Spiral Matrix (The devil is in the details!!!)
48. Rotate Image
42. Trapping Rain Water
1887. Reduction Operations to Make the Array Elements Equal
128. 最长连续序列
296. Best Meeting Point
1329. Sort the Matrix Diagonally
408. Valid Word Abbreviation
2971. Find Polygon With the Largest Perimeter
953. Verifying an Alien Dictionary
2073. Time Needed to Buy Tickets (think about not using simulation)

Missing value, think about space o ( n ) o(n) o(n)

268. 缺失数字, 442. Find All Duplicates in an Array, 41. 缺失的第一个正数

Similar to word count in MapReduce

1578. Minimum Time to Make Rope Colorful

Boyer-Moore Majority Vote

169. Majority Element, 229. Majority Element II

Line Sweep Algorithm

Ref: https://leetcode.com/discuss/study-guide/2166045/line-sweep-algorithms

253. Meeting Rooms II, 632. 最小区间
759. Employee Free Time
218. The Skyline Problem

查找常用字符

区间类

判断区间重叠/overlapped intertals
所有重合的情况是:
在这里插入图片描述
观察发现,重合的条件很简单:
在这里插入图片描述
即:A.start <= B.end and A.end >= B.start

除此之外,还有一种判断方法,就是直接看重合区间是否存在,即overlap_start = max(A.start, B.start), overlap_end = min(A.end, B.end),如果overlap_start <= overlap_end,则重合区间存在

56. 合并区间57. 插入区间986. 区间列表的交集 (判断列表overlap的方法很值得看)、759. Employee Free Time
435. 无重叠区间452. 用最少数量的箭引爆气球

划分字母区间提莫攻击

For weighted interval scheduling, it’s a dp problem, see dp section for further details.

链表

206. 反转链表92. 反转链表 II25. K 个一组翻转链表两两交换链表中的节点

旋转链表

复制带随机指针的链表
86. Partition List
143. 重排链表

2095. Delete the Middle Node of a Linked List19. 删除链表的倒数第N个节点
141. 环形链表(判断链表中是否有环)、142. 环形链表 II(找链表中环的入口)

移除链表元素移除链表中的重复节点

21. 合并两个有序链表23. 合并K个排序链表
Merge In Between Linked Lists

725. Split Linked List in Parts

2. 两数相加两数相加II

146. LRU Cache

队列

3. 无重复字符的最长子串
员工的重要性
950. Reveal Cards In Increasing Order

单调队列

队列里的元素要么单调递增,要么单调递减

239. 滑动窗口最大值
绝对差不超过限制的最长连续子数组

双端搜索

核心思想:寻找图中某个点到某个点是否存在联通的路径,那么可以从起点和中点一起出发,如果2头有重叠的点,则找到联通的路径了(不知道为啥代码写的不对。。。暂时不管这里了)

堆/优先级队列

树状的结构

实现

list + binary search

建堆时间复杂度分析

结论:建堆的时间复杂度:在n个节点上建堆,时间复杂度是 o ( n ) o(n) o(n)
分析:
看最坏的时间复杂度,假设n个节点,高度为h,则第i层有 2 i − 1 2^{i - 1} 2i1个节点,计算建堆的时间复杂度,既可以按照一个一个节点插入堆,然后调整节点来考虑,也可以按照所有节点都随机摆好,然后一个一个调整来计算。这里使用第二种情况考虑,最坏情况下,每个节点都需要考虑调整位置:
h - 1层的节点有 2 h − 1 2^{h - 1} 2h1个,需要调整1次,第h - 2层的节点有 2 h − 2 2^{h - 2} 2h2个,需要调整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=12h1+22h2++(h1)21+h20
求解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=12h2+22h3++(h1)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} 21ss=2h1+2h2++2021h=2h+2h1++2h=122(12h)h=2h+12h
因为 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+12h=2h4h=2log2n4h=n4log2n
所以建堆的复杂度是 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 ( log ⁡ k ) o(\log k) o(logk)的时间),否则继续向后遍历。所以时间复杂度是 o ( n log ⁡ k ) o(n \log k) o(nlogk)

How to build a max-heap for strings?

Use a self defined class like below:

class MaxHeapObj(str):
    def __init__(self, string):
        self.string = string
    def __lt__(self, other):
        return self.string > other.string
    def __eq__(self, other):
        return self.string == other.string

# use MaxHeapObj as a wrapper for string
heapq.heappush(heap, MaxHeapObj(string))

MaxHeapObj is a sub-class of str, and we changed the rule of <.

215. 数组中的第K个最大元素973. 最接近原点的 K 个点
2462. Total Cost to Hire K Workers
253. Meeting Rooms II, 2402. Meeting Rooms III
295. Find Median from Data Stream
1642. Furthest Building You Can Reach
数据流中的第K大元素
347. 前 K 个高频元素
373. Find K Pairs with Smallest Sums
767. 重构字符串
871. 最低加油次数

1871. Jump Game VII
1696. Jump Game VI
630. Course Schedule III
632. 最小区间
621. 任务调度器

Basic questions

20. 有效的括号, 150. 逆波兰表达式求值, 856. Score of Parentheses
1249. Minimum Remove to Make Valid Parentheses
224. 基本计算器 (只有+, -, ()), 227. 基本计算器 II (+, -, *, /,无括号), 772. Basic Calculator III (+, -, *, /, and parentheses)
394. 字符串解码
735. Asteroid Collision, 2751. Robot Collisions
636. Exclusive Time of Functions
678. Valid Parenthesis String (worth thinking)

Monotonic Stack

Basic
155. Min Stack
Advanced
1944. Number of Visible People in a Queue
316. Remove Duplicate Letters(monotonic stack + other tricks)
456. 132 Pattern
84. 柱状图中最大的矩形
907. Sum of Subarray Minimums
402. Remove K Digits
496、901、402、581、238、407、
每日温度

Unsorted

最长有效括号
删除字符串中的所有相邻重复项 II

遍历
前序、中序、后序遍历的递归版写法:

# 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

遍历相关题目

  1. 前序遍历
    二叉树的前序遍历二叉树展开为链表, 1457. Pseudo-Palindromic Paths in a Binary Tree, 314. Binary Tree Vertical Order Traversal
  2. 中序遍历
    二叉搜索树结点最小距离/二叉搜索树的最小绝对差(两题相同)、94. Binary Tree Inorder Traversal、恢复二叉搜索树, 426. Convert Binary Search Tree to Sorted Doubly Linked List
  3. 后序遍历
    二叉树的坡度Evaluate Boolean Binary Tree2265. Count Nodes Equal to Average of Subtree, 1026. Maximum Difference Between Node and Ancestor, 666. Path Sum IV, 543. Diameter of Binary Tree
  4. 层次遍历
    普通层序遍历:
    102. 二叉树的层序遍历员工的重要性111. 二叉树的最小深度
    考虑层数/最右节点的层次遍历:
    103. 二叉树的锯齿形层次遍历
    104. 二叉树的最大深度
    填充每个节点的下一个右侧节点指针(层次遍历+判断是否为本层最右节点)、117. 填充每个节点的下一个右侧节点指针 II(同前)
    199. 二叉树的右视图
    1161. Maximum Level Sum of a Binary Tree
    515, 637,107,1162, 102的所有相似题目
  5. 深度优先遍历
    路径总和路径总和 II路径总和 III(路径+前缀和)
    求根到叶子节点数字之和
    二叉树的所有路径
  6. 广度优先遍历( ≈ \approx 层次遍历)
    101. Symmetric Tree

特殊的二叉树

其他

Binary Tree + Math
780. Reaching Points

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

深度优先遍历DFS

核心思想:栈,入栈前检查是否符合入栈条件,符合则先标记、再入栈。保证栈内元素都符合入栈条件,且已被标记避免重复访问。

leetcode的题目基本思路都差不多,同行题目难度从左向右递增
1306. 跳跃游戏 III
130. 被围绕的区域
200. 岛屿数量695. 岛屿的最大面积463. 岛屿的周长827. Making A Large Island
不同路径 III
785. 判断二分图
矩阵中的最长递增路径
克隆图
图像渲染
79. 单词搜索(其实是用栈模拟了回溯过程)
冗余连接

1034

广度优先遍历BFS

核心思想:队列。由于后放入队列的元素,其step必然更大,所以先出来的元素一定是最短的。

1129. Shortest Path with Alternating Colors1345. 跳跃游戏 IV752. 打开转盘锁
339. Nested List Weight Sum, 364. Nested List Weight Sum II
Minimum Moves to Reach Target with Rotations
542. 01 Matrix
994,310
864. Shortest Path to Get All Keys
1293. Shortest Path in a Grid with Obstacles Elimination

比较特殊的
815. Bus Routes
279. Perfect Squares

BFS+DFS
934. Shortest Bridge

拓扑排序/topological sort

核心思想:每次从入度为0的点开始访问,每访问一个点,就把点的入度-1,如果点的入度被减为0,则把点加入到可访问队列中,直到图中再没有可以访问的点为止

207. 课程表210. 课程表 II
1340. 跳跃游戏 V
冗余连接 II(超时未ac)
2050. Parallel Courses III

路径

字符串

168. Excel Sheet Column Title

14. Longest Common Prefix
1930. Unique Length-3 Palindromic Subsequences
1980. Find Unique Binary String
443. String Compression
1657. Determine if Two Strings Are Close

判断子序列
字典序的第K小数字
Z 字形变换
38. 外观数列
比较含退格的字符串
重构字符串
执行操作后字典序最小的字符串
第N个数字学生出勤记录 I

字符串+数字

8. 字符串转换为整数(atoi)(可用有限自动机解)、7. 整数反转有效数字

匹配

模式匹配正则表达式匹配

dp相关

可见dp中的字符串类

子串相关的问题

139. 单词拆分很值得做)、单词拆分II连接词(WA)

前缀树/字典树/trie tree

前缀树在大量字符串查表过滤的时候很有用
208. 实现 Trie (前缀树)
Remove Sub-Folders from the Filesystem
588. Design In-Memory File System
1268. Search Suggestions System(TrieTree With frequency), 642. Design Search Autocomplete System

回文串/Palindrome

回文串就是中心对称的字符串

回文数125. 验证回文串680. 验证回文字符串 Ⅱ(可删除1个字符)、1216. Valid Palindrome III (delete k characters), 回文链表

5. 最长回文子串最长回文串(利用给出的字符串构造最长回文串)、516. 最长回文子序列由子序列构造的最长回文串的长度最短回文串

分割回文串分割回文串II

647. 回文子串(统计回文子串的个数)

回文对

并查集

冗余连接
带阈值的图连通性
2948. Make Lexicographically Smallest Array by Swapping Elements
785. 判断二分图
2709. Greatest Common Divisor Traversal (ufs + math (gcd/prime factorization)

基础算法

枚举

模式匹配
2232. Minimize Result by Adding Parentheses to Expression

贪心

贪心的题目,一般需要先对数据进行排序,然后从最小的开始。
有时候可能需要证明为什么局部最优解是全局最优解。最方便的方法是反证法:假设不用当前这个顺序,那么新的顺序得到的值,比按照贪心的顺序得到的值更小,更无法满足要求。

分发饼干

135. 分发糖果(贪心+分治)

2366. Minimum Replacements to Sort the Array

55. 跳跃游戏45. 跳跃游戏 II1326. 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
1665. Minimum Initial Energy to Finish Tasks
1024
1963. Minimum Number of Swaps to Make the String Balanced
452. 用最少数量的箭引爆气球
621. 任务调度器

1921. Eliminate Maximum Number of Monsters

2939. Maximum Xor Product

区间系列
Interval scheduling: earliest finish first
435. 无重叠区间646. Maximum Length of Pair Chain

  • 进阶版weighted interval scheduling, 见区间dp

Interval partitioning: earliest start first
2406. Divide Intervals Into Minimum Number of Groups

子序列系列
334. 递增的三元子序列(结合LIS)
300. 最长上升子序列(贪心+二分,必做)、354. Russian Doll Envelopes
630. Course Schedule III

分治

分发糖果(贪心+分治)
4. Median of Two Sorted Arrays

回溯+递归

core function is f(already_done, remaining_variables). The problems could be sorted into these categories:

CategoryTime ComplexitySpace Complexity
subset o ( n ∗ 2 n ) o(n*2^n) o(n2n)
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(Cnkk)
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 n2n
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 Traversal106. Construct Binary Tree from Inorder and Postorder Traversal
22. 括号生成
93. 复原IP地址
39. 组合总和40. 组合总和 II216. 组合总和 III377. 组合总和 Ⅳ(follow-up)
77. 组合
46. Permutations47. Permutations II
78. Subsets
51. N皇后52. N皇后 II
37. Sudoku Solver
1239. Maximum Length of a Concatenated String with Unique Characters
465. Optimal Account Balancing
576. Out of Boundary Paths
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. 预测赢家
1463. Cherry Pickup II

划分为k个相等的子集火柴拼正方形(思路和前面完全一样,但是更简单)
301. Remove Invalid Parentheses
90, 47, 31, 60

不同的二叉搜索树 II
最大二叉树(递归)
戳气球(超时)、移除盒子(超时)
38. 外观数列
24 点游戏
60. 第k个排列

排序

python自带的sort用了timesort,平均时间复杂度是 o ( n l o g n ) o(nlogn) o(nlogn)
练习各类排序的题:912. 排序数组

280. Wiggle Sort, 324. 摆动排序 II

常见的排序比较

排序方法主要思想时间复杂度stability
insertion sort无序的数字向有序列表中插 o ( n 2 ) o(n^2) o(n2)稳定
binary insertion sort无序的数字二分法向有序列表中插 o ( n log ⁡ n ) o(n\log n) o(nlogn)稳定
bubble sort每次遍历都找出来最大/最小的元素 o ( n 2 ) o(n^2) o(n2)稳定
selection sort每次遍历找出最小/最大元素,和当前值交换位置 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)不稳定
quick sort每次排序都排好某个数,让比这个数小的数都在这个数左边,比这个数大的数都在这个数右边,实现可见这里 o ( n l o g n ) o(nlogn) o(nlogn)不稳定
堆排序 o ( n l o g n ) o(nlogn) o(nlogn)不稳定
merge sort排左边,排右边,再合并有序数组 o ( n l o g n ) o(nlogn) o(nlogn)稳定
counting sortUse auxiliary list to keep track of how many numbers there are that are not larger than current element o ( n + k ) o(n+k) o(n+k), where k is the number range in listStable

应用场景及题目

排序方法常用场景相关题目
直接插入排序
折半插入排序
希尔排序
冒泡排序找到最大值
快速排序所有排序题目,建议首选215. 数组中的第K个最大元素
堆排序
归并排序链表排序合并两个有序数组、合并两个有序链表23. 合并K个排序链表
排序链表493. 翻转对

快排实现
递归版,核心思想是分治法
时间复杂度:平均 o ( n log ⁡ n ) o(n\log n) o(nlogn),最差 o ( n 2 ) o(n^2) o(n2),不稳定
思想:每次排好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)

非递归版,core idea is Dutch national flag problem.

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        def helper(left: int, right: int) -> None:
            if left >= right:
                return
            pivot = nums[random.randint(left, right)]
            small_index, large_index = left, right
            p = left
            while p <= large_index:
                if nums[p] < pivot:
                    nums[small_index], nums[p] = nums[p], nums[small_index]
                    small_index += 1
                    p += 1
                elif nums[p] > pivot:
                    nums[large_index], nums[p] = nums[p], nums[large_index]
                    large_index -= 1
                else:
                    p += 1
            helper(left, small_index - 1)
            helper(large_index + 1, right)
        helper(0, len(nums) - 1)
        return nums

三色旗问题
一个很有意思的排序问题,核心思想是保存最小的数字指针和最大的数字指针。题目是75. 颜色分类

查找/二分法

二分查找
核心思想:丢弃不符合要求的一半数据,在剩下的一半中继续寻找
关键点:

  1. left, right的选取:left, right代表要搜索的空间范围,正常来说,left = 0, right = len(nums)-1,但比如是插入的题,那么right = len(nums)
  2. 退出循环的条件:while left < right
  3. 最终返回值:更新后的mid。需要注意,实际上最终的结果应该是mid,但因为大部分情况下退出都是因为left=right,所以直接返回left或者right即可。但如果是插入题,初始化right = len(nums),同时mid取值选了右边界,可能会导致超出取数的索引,此时需要额外处理
  4. 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章节

基本题目(easy)

704. 二分查找35. 搜索插入位置374. 猜数字大小
旋转数组/peak/mountain
33. 搜索旋转排序数组81. 搜索旋转排序数组ii(旋转数组+重复数字)
153. 寻找旋转排序数组中的最小值154. 寻找旋转排序数组中的最小值 II
852. Peak Index in a Mountain Array, 162. Find Peak Element, 1901. Find a Peak Element II, 1095. Find in Mountain Array

2D array

74. Search a 2D Matrix240. Search a 2D Matrix II
1428. Leftmost Column with at Least a One

Math

69. Sqrt(x)

The sorted list is not given explicitly(TODO)

See similar question in this post
378. 有序矩阵中第K小的元素, 719. Find K-th Smallest Pair Distance, 1802. Maximum Value at a Given Index in a Bounded Array

Discard halfs

4. Median of Two Sorted Arrays

转变数组后最接近目标值的数组和(比较的数字不是直接给出的,而要经过转换)

第一个错误的版本(找左边界)、在排序数组中查找元素的第一个和最后一个位置(找左、右边界)、找到 K 个最接近的元素(找左边界、再线性扫描)
528. Random Pick with Weight

值得一做:275. H-Index II2616. Minimize the Maximum Difference of Pairs

1870. Minimum Speed to Arrive on Time
2251. Number of Flowers in Full Bloom
1268. Search Suggestions System(String binary search)

410,774,875,1011,1231,1283,1482,1539,1802,2226,2560,2616

二分插入
计算右侧小于当前元素的个数

动态规划

分类参考了这篇博客: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. 预测赢家
823. Binary Trees With Factors
91. Decode Ways, 1155. Number of Dice Rolls With Target Sum
1531. String Compression II
Maximum Number of Consecutive Values You Can Make
杨辉三角杨辉三角 II
不相交的线
62. 不同路径63. 不同路径 II
920. Number of Music Playlists
地下城游戏
三角形最小路径和
不同的二叉搜索树
学生出勤记录 II
935. Knight Dialer
446. Arithmetic Slices II - Subsequence
926. Flip String to Monotone Increasing
629. K Inverse Pairs Array
目标和
279. Perfect Squares

最大平均值和的分组分割数组的最大值

整数拆分、剪绳子

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

LIS(最长递增序列)

300. 最长上升子序列354. Russian Doll Envelopes
乘积最大子数组
646, 673, 712

递增子序列
368. Largest Divisible Subset

LCS(最长公共子序列)

1143. 最长公共子序列516. 最长回文子序列
最长重复子数组

树形dp

124. 二叉树中的最大路径和二叉树的坡度

树中距离之和

区间dp

weighted interval scheduling
1235. Maximum Profit in Job Scheduling2830. 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. 零钱兑换
2742

股票买卖系列

参考题解: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=0iy=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[i1][j]+b[i][j1]b[i1][j1]+a[i][j]

前缀和常见的题目,一般是求满足xx条件的子数组,注意子数组/subarray和子序列/subsequence之前的区别。

子数组/subarray: 连续的
子序列/subsequence: 可以不连续

前缀和的核心点在于,确定好相减这个操作的物理意义,尤其是被减掉的那个子数组。通常情况下,被减掉的子数组没有特殊要求,如560. 和为K的子数组等。但也有特定要求的子数组,如2488. Count Subarrays With Median K,就要求被减掉的数组是在本题中的k左边以保证中位数

238. Product of Array Except Self
1685. Sum of Absolute Differences in a Sorted Array
1248. Count Number of Nice Subarrays

hard, worth thinking
1871. Jump Game VII

位运算/Bit Manipulation

求二进制中1的个数(x & x - 1, eliminate the rightest 1)
两数相除
389. Find the Difference
136. 只出现一次的数字(异或解法、通用解法)、137. 只出现一次的数字 II(数学解法)、260. Single Number III

Power of xxx: 231(power of 2), 342. Power of Four, 326. Power of Three
Common method: binary search
If it’s a prime number: prime factorization
If it’s 2 or 4: special cases

50. Pow(x, n), 372. Super Pow
191. Number of 1 Bits201. 数字范围按位与
汉明距离

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
319. Bulb Switcher
311. Sparse Matrix Multiplication
2849. Determine if a Cell Is Reachable at a Given Time
1759. Count Number of Homogenous Substrings
2038. Remove Colored Pieces if Both Neighbors are the Same Color
1561. Maximum Number of Coins You Can Get
1464. Maximum Product of Two Elements in an Array

gcd

2709. Greatest Common Divisor Traversal

prime factorization

2709. Greatest Common Divisor Traversal

x数之和

最基础、最简单的就是1. 两数之和,核心思想:用哈希表查询target - num

接下来以此为基础的题目,在数字上变形的,有:

在输入形式上变形的,有:

自己实现加减乘除

加法

  1. 核心思想:模拟人脑加法,从右向左、保持进位;利用数学性质,从左向右、每次结果*10再相加
    题目:两数相加445. 两数相加II、加一、67. 二进制求和字符串相加
  2. 核心思想:位运算
    题目:两整数之和

乘法

核心思想:从左向右加,每次结果乘10
题目:字符串相乘

计算器

基本计算器(只有+, -, ())、基本计算器 II+, -, *, /,无括号)(内含有括号的代码)

滑动窗口

滑动窗口适用于这类题目:

  1. 求满足某条件的子数组(consecutive)
  2. 数组中只包含正数(可以通过缩减窗口来保证和变小,如果还有负数,则可能通过增大窗口,也能让和变小)

209. 长度最小的子数组3. 无重复字符的最长子串
76. Minimum Window Substring

1493. Longest Subarray of 1‘s After Deleting One Element2024. Maximize the Confusion of an Exam

2090. K Radius Subarray Averages
239

438. Find All Anagrams in a String
1838. Frequency of the Most Frequent Element
2264. Largest 3-Same-Digit Number in String
1658. Minimum Operations to Reduce X to Zero

487. Max Consecutive Ones II, 1004. Max Consecutive Ones III

Number of Subarray
2302. Count Subarrays With Score Less Than K, 713. Subarray Product Less Than K, 2762. Continuous Subarrays
992. K个不同整数的子数组, 1248. Count Number of Nice Subarrays
2962. Count Subarrays Where Max Element Appears at Least K Times (different way to calculate the number of subarray)
2444. Count Subarrays With Fixed Bounds (3 pointers)

排列生成

31. 下一个排列

设计类题目

感觉这类题目出得好的很有难度,出得一般的写起来怪怪的。。。
设计推特
Design Authentication Manager

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值