Go 数据结构与算法、SQL 刷题 ⌨️
刷题记录和总结
数组
题号 | 思路 | 备注 |
---|---|---|
704. 二分查找 | 有序无重是前提,左右指针,不断缩小查找范围,注意边界处理 | 1️⃣ |
27. 移除元素 | 快慢指针替换位置 | 1️⃣ |
977. 有序数组的平方 | 两头对比选择,从后往前填充 | 2️⃣ |
209. 长度最小的子数组 | 双指针滑动收缩窗口 | 2️⃣ |
59. 螺旋矩阵 II | 模拟过程,一圈圈往里缩,注意统一 [i, j) 填充范围 | 2️⃣ |
15. 三数之和 | 排序 + 双指针 + 剪枝 | ✅ |
18. 四数之和 | 排序 + 双指针 + 剪枝(上一题多加一个 for) | ✅ |
链表
题号 | 思路 | 备注 |
---|---|---|
203. 移除链表元素 | 设置虚拟头节点,统一处理 | 3️⃣ |
707. 设计链表 | 注意 for 后 cur 的定位 | 3️⃣ |
206. 反转链表 | 双指针同步移动,注意循环终止条件 | 3️⃣ |
24. 两两交换链表中的节点 | 虚拟头节点 + 两个记录节点 | 4️⃣ |
19. 删除链表的倒数第 N 个结点 | 虚拟头节点,快慢指针 | 4️⃣ |
面试题 02.07. 链表相交 | 双指针走完互换起点,结尾的 nil 也可看做是交点 | 4️⃣ |
142. 环形链表 II | 快慢指针环内相遇后,起点位置和慢指针同步移动一定会相遇 | 4️⃣ |
哈希表
题号 | 思路 | 备注 |
---|---|---|
242. 有效的字母异位词 | 分别遍历,先 ++ 后 –,最后判断 | 5️⃣ |
349. 两个数组的交集 | 分别遍历,判断收集去重 | 5️⃣ |
202. 快乐数 | 模拟,map 判断是否出现过 | 5️⃣ |
1. 两数之和 | 遍历,一边判断一边存 [val] index | 5️⃣ |
454. 四数相加 II | 两个双重循环,用 map 优化 | 6️⃣ |
383. 赎金信 | ++ 后 –,– 时判断是否 < 0 | 6️⃣ |
字符串
题号 | 思路 | 备注 |
---|---|---|
344. 反转字符串 | 双指针,往里夹 | 7️⃣ |
541. 反转字符串 II | 局部的反转 + 最后的边界判断 | 7️⃣ |
151. 反转字符串中的单词 | 双指针删除冗余空格/定位单词 + 反转整体再反转每个单词 | 8️⃣ |
28. 找出字符串中第一个匹配项的下标 | Knuth-Morris-Pratt 字符串查找算法 | 🤡 $O(n * m)$ → $O(n + m)$ |
459. 重复的子字符串 | KMP 基础上,首尾拼接查中间 | 🤡 |
栈与队列
题号 | 思路 | 备注 |
---|---|---|
225. 用队列实现栈 | 使用两个 slice ,每添加一个元素都需要倒腾 |
9️⃣ |
232. 用栈实现队列 | 输入栈 + 输出栈,出的时候触发倒腾 | 9️⃣ |
20. 有效的括号 | 用 slice 模拟栈,遍历时添加或消除括号 |
9️⃣ |
1047. 删除字符串中的所有相邻重复项 | 在栈中玩消消乐 | 9️⃣ |
150. 逆波兰表达式求值 | “后缀表达式” 求解,用栈模拟 | 1️⃣0️⃣ |
239. 滑动窗口最大值 | 实现 “单调队列”,记录窗口内的最大值 | 1️⃣0️⃣ |
347. 前 K 个高频元素 | “优先队列”(添加和移除元素的时候维护单调性);统计后排序(偷懒做法) | 1️⃣0️⃣ |
单调栈
题号 | 思路 | 备注 |
---|---|---|
739. 每日温度 | 栈顶到栈底递增(即遇到 第一个 更高温,才开始更新等待的天数) | 4️⃣1️⃣ |
496. 下一个更大元素 I | 上一题套了一个哈希映射 | 4️⃣1️⃣ |
503. 下一个更大元素 II | 在遍历的过程中使用 % 模拟走两遍 nums |
4️⃣1️⃣ |
42. 接雨水 | 凹,横着计算面积,算完一个面积后记得更新栈顶元素 | 4️⃣2️⃣ |
84. 柱状图中最大的矩形 | 凸,数组头尾加个 0 避免数组原来就有序不走计算逻辑和拿不到 left | 4️⃣2️⃣ |
二叉树
题号 | 思路 | 备注 |
---|---|---|
144. 二叉树的前序遍历,94. 中,145. 后 | 递归(注意 slice 是一个值类型);迭代用个栈,注意中序的访问和处理节点不一致;后序为【左右中】即【中右左】的反转 | 1️⃣1️⃣ |
102. 二叉树的层序遍历 | 使用 []*TreeNode 保存每一层节点 |
1️⃣1️⃣ |
226. 翻转二叉树 | 每个节点的左右都交换一下即整棵树翻转(中序遍历不行) | 1️⃣2️⃣ |
101. 对称二叉树 | 只能后序遍历(左右中 + 右左中) | 1️⃣2️⃣ |
104. 二叉树的最大深度 | 根节点的高度就是二叉树的最大深度(后序) | 1️⃣2️⃣ |
111. 二叉树的最小深度 | 和上一题对比,区别在于处理非叶子节点的逻辑(后序) | 1️⃣2️⃣ |
110.平衡二叉树 | 计算以每个节点为根节点的二叉树的高度(后序) | 1️⃣3️⃣ |
257. 二叉树的所有路径 | 前序遍历,字符串的值传递隐含了回溯(先序) | 1️⃣3️⃣ |
404. 左叶子之和 | 通过节点的父节点来判断其左孩子是不是左叶子 | 1️⃣3️⃣ |
222. 完全二叉树的节点个数 | 可以利用完全二叉树的特性,公式求解(后序) | 1️⃣4️⃣ |
513. 找树左下角的值 | 递归:深度最大的叶子节点是最后一行(先序);迭代:模板 | 1️⃣4️⃣ |
112. 路径总和 | 函数的值传递隐含了回溯,递归函数需要返回值 | 1️⃣4️⃣ |
113. 路径总和 II | 递归函数不需要返回值,注意指针类型会被修改要 copy 一下 | 1️⃣4️⃣ |
106. 从中序与后序遍历序列构造二叉树 | 用散列表记录元素再前序中的索引,根据子树元素个数切割数组,[l, r] | 1️⃣4️⃣ |
654. 最大二叉树 | 前序遍历递归构建 | 1️⃣5️⃣ |
617. 合并二叉树 | 同时遍历两棵树,递归的优雅表现 | 1️⃣5️⃣ |
700.二叉搜索树中的搜索 | 利用二叉搜索树的性质 | 1️⃣5️⃣ |
98.验证二叉搜索树 | 注意,不能陷入只判断局部(先序) | 1️⃣5️⃣ |
236. 二叉树的最近公共祖先 | 遍历整棵树,判断两种情况(后序) | 1️⃣6️⃣ |
501. 二叉搜索树中的众数 | 计数法不使用额外空间, prev 指针(中序) |
1️⃣6️⃣ |
530. 二叉搜索树的最小绝对差 | 在一个有序数组上求差值(中序) | 1️⃣6️⃣ |
235. 二叉搜索树的最近公共祖先 | 遇到 cur 节点数值在 [q, p] 区间中,即是最近公共祖先(往一边走就行,后序) | 1️⃣7️⃣ |
701.二叉搜索树中的插入操作 | 通过递归返回值来加入新节点,不涉及结构的调整(后序查找目标位置) | 1️⃣7️⃣ |
450.删除二叉搜索树中的节点 | 删除节点操作涉及到结构的调整(后序查找目标位置) | 1️⃣7️⃣ |
669. 修剪二叉搜索树 | 不需要删除,只需要做结构的调整 | 1️⃣8️⃣ |
108. 将有序数组转换为二叉搜索树 | 寻找分割点 len(nums) / 2 ,递归左区间和右区间 |
1️⃣8️⃣ |
538. 把二叉搜索树转换为累加树 | 即有序数组求 从后到前 的累加数组,反中序(右左中)遍历顺序累加 | 1️⃣8️⃣ |
回溯
题号 | 思路 | 备注 |
---|---|---|
77. 组合 | 注意切片里面是指针不能直接放,要 copy 一下(组合不考虑顺序) | 1️⃣9️⃣ |
216.组合总和 III | 比上一题多了一个判断的条件,注意剪枝 | 1️⃣9️⃣ |
17. 电话号码的字母组合 | 构建映射,不同集合,所以 i 都是从 0 开始 | 1️⃣9️⃣ |
39. 组合总和 | 可重复选取,不用控制遍历的起始位置+1;排序剪枝 | 2️⃣0️⃣ |
40. 组合总和 II | 只能选取一次,在回溯中对同一层的元素去重,而不对同一枝去重 | 2️⃣0️⃣ |
131. 分割回文串 | 切割线,切割问题抽象为组合问题 | 2️⃣0️⃣ |
93. 复原 IP 地址 | 也是切割问题,终止条件判断不一样 | 2️⃣1️⃣ |
78. 子集 | 集合无重复元素,无条件收集所有节点,无需剪枝 | 2️⃣1️⃣ |
90. 子集 II | 集合有重复元素,排序、同层去重、同枝不去 | 2️⃣1️⃣ |
491. 非递减子序列 | 不能排序,对同枝元素去重,注意 used 数组的位置,只负责本层 | 2️⃣2️⃣ |
46. 全排列 | 不包含重复元素,不需要 startIndex,需要标记一个排列里使用过的元素 | 2️⃣2️⃣ |
47. 全排列 II | 包含重复元素,排序、同层去重、同枝不去 | 2️⃣2️⃣ |
332. 重新安排行程 | 🤡 | |
51. N 皇后 | 🤡 | |
37. 解数独 | 🤡 |
贪心
题号 | 思路 | 备注 |
---|---|---|
455. 分发饼干 | 两者都排序后,胃口主动遍历,饼干的被动(优先满足胃口大的) | 2️⃣3️⃣ |
376. 摆动序列 | “删除”单调坡度上的中间节点(统计峰值节点) | 2️⃣3️⃣ |
53. 最大子数组和 | 当前“连续和”为负数的时候立刻放弃 | 2️⃣3️⃣ |
122. 买卖股票的最佳时机 II | 只收集相邻的每个正利润 | 2️⃣4️⃣ |
55. 跳跃游戏 | 判断跳跃覆盖范围能不能覆盖到终点 | 2️⃣4️⃣ |
45. 跳跃游戏 II | 每超过上一次可达最大范围,需要跳跃次数 +1 后扩大范围 | 2️⃣4️⃣ |
1005. K 次取反后最大化的数组和 | 按绝对值进行排序 | 2️⃣4️⃣ |
134. 加油站 | 当前累加 rest[i] 的和 curSum 一旦小于 0,起始位置至少要是 i+1 |
2️⃣5️⃣ |
135. 分发糖果 | 先 → 比 右 > 左,再 ← 比 左 > 右 | 2️⃣5️⃣ |
860. 柠檬水找零 | 简单模拟 | 2️⃣5️⃣ |
406. 根据身高重建队列 | 按照身高大到小、人数小到大排序,优先按身高高的 k 来插入 | 2️⃣5️⃣ |
452. 用最少数量的箭引爆气球 | 出现重叠的气球只需要一个箭;排序后,判断重叠区间,统计箭数 | 2️⃣6️⃣ |
435. 无重叠区间 | 按右界排序,用总数减去非重叠区间的个数;修改范围模拟移除 | 2️⃣6️⃣ |
763. 划分字母区间 | 字符最远出现位置下标和当前下标相等时即为分割点 | 2️⃣6️⃣ |
56. 合并区间 | 排序、遍历、比较(拿结果集的最后一个元素)、合并 | 2️⃣7️⃣ |
738. 单调递增的数字 | 后往前遍历,-1 时触发后面范围都置 9 | 2️⃣7️⃣ |
968. 监控二叉树 | 🤡 |
动态规划
题号 | 思路 | 备注 |
---|---|---|
509. 斐波那契数 | 感受一下 | 2️⃣8️⃣ |
70. 爬楼梯 | 上一题套了一个壳 | 2️⃣8️⃣ |
746. 使用最小花费爬楼梯 | 到达第 i 台阶所花费的最少体力为 dp[i] |
2️⃣8️⃣ |
62.不同路径 | 从 (0, 0) 出发,到 (i, j) 有 dp[i][j] 条不同的路径 |
2️⃣9️⃣ |
63. 不同路径 II | 障碍标记对应的 dp[i][j] 保持初始值(0) |
2️⃣9️⃣ |
343. 整数拆分 | 分拆正整数 i 可以得到的最大乘积为 dp[i] ,max((i - j) * j), dp[i - j] * j) |
2️⃣9️⃣ |
96. 不同的二叉搜索树 | 1 - i 为节点组成的二叉搜索树的个数为 dp [i],递推公式不好找 | 🤡 |
01 背包(二维/一维) | 每个物品只有一个,一维需要从大小遍历背包 | 3️⃣0️⃣ |
416. 分割等和子集 | 背包大小为 sum / 2 ,商品大小和价值一样,若 dp[target] == target 则存在 (装满背包的最大价值/重量) |
3️⃣0️⃣ |
1049. 最后一块石头的重量 II | 让石头分成重量最相同的两堆,相撞之后剩下的石头最小 (装满背包的最大价值/重量) | 3️⃣1️⃣ |
494. 目标和 | dp[j] 表示填满 j(包括 j)这么大容积的包,有 dp[j] 种方法 (装满背包的组合数) |
🤨 |
474. 一和零 | 物品的重量/背包 有两个维度 (背包可以装的最大物品数量) 🔢 | 3️⃣1️⃣ |
完全背包 | 每件物品都有无限个,一维需要从小到大遍历背包 | 3️⃣1️⃣ |
518. 零钱兑换 II | 凑成总金额 j 的货币组合数为 dp[j] (装满背包的组合数) |
3️⃣2️⃣ |
377. 组合总和 Ⅳ | 注意是“排列”数,需要先遍历背包再遍历物品 (装满背包的排列数) | 3️⃣2️⃣ |
322. 零钱兑换 | 凑足总额为 j 所需钱币的最少个数为 dp[j] (装满背包的最小物品数量,加是否装满的判断)🔢 |
3️⃣3️⃣ |
279. 完全平方数 | 和为 j 的完全平方数的最少数量为 dp [j],注意初始化 (装满背包的最小物品数量)🔢 | 3️⃣3️⃣ |
139. 单词拆分 | wordDict 是物品,s 是背包,物品能不能装入是看能不能和背包后缀匹配 (装满背包的排列数) | 3️⃣3️⃣ |
打家劫舍 | ||
198. 打家劫舍 | 考虑下标 i(包括 i)以内的房屋,最多可以偷窃的金额为 dp [i] |
3️⃣4️⃣ |
213. 打家劫舍 II | 包含首元素,不包含尾元素;包含尾元素,不包含首元素;两种情况取最大值 | 3️⃣4️⃣ |
337. 打家劫舍 III | dp 数组下标为 0 记录不偷该节点所得到的的最大金钱,下标为 1 记录偷该节点所得到的的最大金钱 (后序遍历) | 3️⃣4️⃣ |
买卖股票 | 买入卖出,但是同时只能持有一支股票 | |
121. 买卖股票的最佳时机 | dp[i][0] 表示第 i 天不持有,dp[i][1] 表示第 i 天持有股票所得最多现金 (只能买卖一次) |
3️⃣5️⃣ |
122.买卖股票的最佳时机 II | 可以完成多笔交易,最多只能持有一股股票 (可以买卖多次) | 3️⃣5️⃣ |
123.买卖股票的最佳时机 III | 可以完成最多两笔交易,不能同时 (可以买卖最多两次) | 3️⃣5️⃣ |
188. 买卖股票的最佳时机 IV | 可以完成最多 k 笔交易,,除了 0 以外,偶数就是卖出,奇数就是买入 (可以买卖最多 k 次) | 3️⃣6️⃣ |
309. 买卖股票的最佳时机含冷冻期 | 卖出股票后,你无法在第二天买入股票,即冷冻期为 1 天 (可以买卖最多次) | 3️⃣6️⃣ |
714. 买卖股票的最佳时机含手续费 | 卖出的时候加个手续费即可 (可以买卖多次) | 3️⃣6️⃣ |
子序列问题 | ||
300.最长递增子序列 | dp[i] 表示 i 之前包括 i 的以 nums[i] 结尾的最长递增子序列的长度 |
3️⃣7️⃣ |
674. 最长连续递增序列 | 只要比较 nums[i] 与 nums[i - 1] ,而不用去比较 nums[j] 与 nums[i] (j 是在 0 到 i 之间遍历) |
3️⃣7️⃣ |
718. 最长重复子数组 | 子数组就是连续子序列,以下标 i 为结尾的 A,和以下标 j 为结尾的 B,最长重复子数组长度为 dp[i][j] ,注意初始化 |
3️⃣7️⃣ |
1143.最长公共子序列 | 较上一题就是少了“连续”的要求,如果不等取各自前一个状态的最大值,注意初始化和上面有不同,两者都可以删除元素 | 3️⃣8️⃣ |
1035.不相交的线 | 与上一题相同,只要相对顺序不变就行(就是求最长公共子序列) | 3️⃣8️⃣ |
53. 最大子数组和 | (连续)子数组,dp[i] 表示以 i 为结尾的子数组的最大和 |
3️⃣8️⃣ |
392. 判断子序列 | 以下标 i 为结尾的 s,和以下标 j 为结尾的 t,相同子序列的长度为 dp[i][j] ,删除元素只能是 t(判断最长公共子序列长度是否和 t 长度一样即可) |
3️⃣8️⃣ |
115. 不同的子序列 | 以下标 i 为结尾的 s 子序列中出现以 j 为结尾的 t 的个数为 dp[i][j] ,较上一题初始化和递推公式不同 |
3️⃣9️⃣ |
583. 两个字符串的删除操作 | 找出最长公共子序列,用两个字符串的总长度减去两个最长公共子序列的长度 | 3️⃣9️⃣ |
72. 编辑距离 | 3️⃣9️⃣ | |
647. 回文子串 | 4️⃣0️⃣ | |
516. 最长回文子序列 | 4️⃣0️⃣ |
图论
题号 | 思路 | 备注 |
---|---|---|
LC🔥100
题号 | 思路 | 备注 |
---|---|---|
哈希 | ||
1. 两数之和 | 哈希(key: val,val: index) | |
49. 字母异位词分组 | map[string][]string 、排序每个元素后作为 key |
|
128. 最长连续序列 | map 用来快速查询某个元素是否存在并去重优化 | |
双指针 | ||
283. 移动零 | 快慢指针,移动非 0,末尾置 0 | |
11. 盛最多水的容器 | 左右指针往里夹,短板效应 | |
15. 三数之和 | 排序去重,一数外循环,两数左右指针往里夹 | |
18. 四数之和 | 套个 for + 三数之和 | |
42. 接雨水 | 维护单调栈(栈底到栈顶递减),识别凹槽,持续按照行计算 ⬅️ | |
滑动窗口 | ||
3. 无重复字符的最长子串 | 一边遍历一边使用 map 记录字符的最后位置,滑动窗口 | |
438. 找到字符串中所有字母异位词 | 滑动窗口 + [] int 记录判断是否为异位词 | |
子串 | ||
560. 和为 K 的子数组 | 前缀和 + 哈希表优化(记录每个前缀和出现的次数) | |
239. 滑动窗口最大值 | 实现 “单调队列”,记录窗口内的最大值 | |
76. 最小覆盖子串 | 哈希表记录,覆盖到后缩小范围,移除无用元素 | |
普通数组 | ||
53. 最大子数组和 | 贪心:遇到负数直接置零;动规:取考虑 num [i] 后的最大值 | |
56. 合并区间 | 排序后遍历,判断修改右边界 or 直接添加 | |
189. 轮转数组 | 整体翻转后再翻转两个局部 | |
238. 除自身以外数组的乘积 | 遍历两次,计算每个元素左侧/右侧乘积 | |
41. 缺失的第一个正数 | 把每个数放在 nums[nums[i] - 1] 的位置上,再遍历检查 |
|
矩阵 | ||
73. 矩阵置零 | 使用第一行/列记录该行/列是否有 0,再遍历一次判断置 0 | |
54. 螺旋矩阵 | 定义好四个边界,模拟 | |
48. 旋转图像 | 先转置(行列交换)再左右对称交换位置 | |
240. 搜索二维矩阵 II | 初始定位到第一行的最后一个元素,之后遍历判断移动行/列 | |
链表 | ||
160. 相交链表 | 到头了就走一遍对方的路 | |
206. 反转链表 | pre cur temp | |
234. 回文链表 | 快慢指针找到链表中点,反转后半部分后逐个与前半部分比较 | $O(1)$ 空间 |
141. 环形链表 | 快慢指针,如果有环一定回相遇,有一个为 nil 就算没环 | |
142. 环形链表 II | 快慢相遇后,头节点和慢指针同步移动,返回相遇点;或者哈希 | |
21. 合并两个有序链表 | 模拟合并,判断处理剩余部分 | |
2. 两数相加 | 模拟相加,记录进位,节点为 nil 给个默认值 0 | |
19. 删除链表的倒数第 N 个结点 | 快慢指针,fast 先移动,到位后一起移动 slow 定位到待删除节点前一个位置 | |
24. 两两交换链表中的节点 | 设置虚拟头节点加两个临时记录的指针 | |
25. K 个一组翻转链表 | 反转链表(head, tail → tail, head),上一个子链的 tail 是下一个的 hair | |
138. 随机链表的复制 | map[*Node]*Node 建立新旧节点的映射关系,map [旧] → 新 |
|
148. 排序链表 | 归并排序,快慢指针找到中点,合并两个有序链表 | |
23. 合并 K 个升序链表 | ||
146. LRU 缓存 | 双向链表 + 哈希表 | |
二叉树 | ||
94. 二叉树的中序遍历 | 递归/迭代(用个栈优先向左子树方向保存节点) | |
104. 二叉树的最大深度 | 后序递归,depth := max(leftDepth, rightDepth) + 1 |
|
226. 翻转二叉树 | 后序递归,翻转每一个节点的左右孩子 | |
101. 对称二叉树 | dfs(left.Left, right.Right) && dfs(left.Right, right.Left) |
|
543. 二叉树的直径 | 后序递归,和二叉树最大深度是一样的,中间多了一个判断收集结果 | |
102. 二叉树的层序遍历 | 用两个队列,curLevel 和 nextLevel 轮换,两个 for |
|
108. 将有序数组转换为二叉搜索树 | 前序遍历,idx := len(nums)/2 |
|
98. 验证二叉搜索树 | 前序遍历,检查的时候需要传递节点的 | |
230. 二叉搜索树中第 K 小的元素 | 中序遍历是有序的,一边遍历一遍计算是否到第 K 个数 | |
199. 二叉树的右视图 | 层序列遍历只收集每一层的最后一个节点 | |
114. 二叉树展开为链表 | 前序遍历后构建链表/前序遍历和展开同步进行 | |
105. 从前序与中序遍历序列构造二叉树 | 查找划分、递归构造 | |
437. 路径总和 III | 递归遍历每个节点,并在每个节点上去查找路径 | |
236. 二叉树的最近公共祖先 | 后序遍历,使用左右子树的递归结果来决定当前节点是否是最近公共祖先 | |
124. 二叉树中的最大路径和 | 后序遍历,注意向上传递的额时候只能传递左右的一边 | |
图论 | ||
200. 岛屿数量 | 遇到一个岛屿则 DFS/BFS(单源) 进行标记 | |
994. 腐烂的橘子 | 多源 BFS(队列里面同时有多个元素),烂 🍊 入队并统计新 🍊 数量 | |
207. 课程表 | 检测课程之间的依赖关系是否存在环(DFS 构建邻接表 /BFS 移除入度为 0 的节点) | |
208. 实现 Trie (前缀树) | 注意结构体的定义,其余都是遍历判断每个字符 | |
回溯 | ||
46. 全排列 | 排列问题,可回头,不需要 start,使用 used 避免重复选择一个数字 |
|
78. 子集 | 组合问题,不可回头,需要遍历下标 start | |
17. 电话号码的字母组合 | 组合问题,构建映射表,需要遍历下标 start | |
39. 组合总和 | 用 start 避免重复组合和支持无限次取,可排序剪枝 | |
22. 括号生成 | ||
79. 单词搜索 | ||
131. 分割回文串 | ||
51. N 皇后 | ||
二分查找 | ||
35. 搜索插入位置 | ||
74. 搜索二维矩阵 | ||
34. 在排序数组中查找元素的第一个和最后一个位置 | ||
33. 搜索旋转排序数组 | ||
153. 寻找旋转排序数组中的最小值 | ||
4. 寻找两个正序数组的中位数 | ||
栈 | ||
20. 有效的括号 | 左括号入栈,右括号优先匹配或入栈,最后判断栈是否为空 | |
155. 最小栈 | 维护两个栈,一个用于存储所有的元素,另一个用于维护历史最小值 | |
394. 字符串解码 | 一个栈用于存储重复次数 k ,另一个栈用于存储当前累积的字符串部分 |
|
739. 每日温度 | 维护一个单调递减栈(栈底到栈顶是递减的,栈顶是最小的), 栈中存储的是索引 | $O(n)$ |
84. 柱状图中最大的矩形 | 维护一个单调递增栈(栈底到栈顶是递增的,栈顶是最大的) | |
堆 | ||
215. 数组中的第 K 个最大元素 | ||
347. 前 K 个高频元素 | ||
295. 数据流的中位数 | ||
贪心算法 | ||
121. 买卖股票的最佳时机 | 一次遍历模拟时间流逝,不断交易取最值 | |
55. 跳跃游戏 | 维护最大覆盖范围,遍历判断 | |
45. 跳跃游戏 II | 维护当前遍历位置最远可达,如果遍历位置到达最远可达则 jump++ |
|
763. 划分字母区间 | 遍历字符串并记录每个字符的最后出现位置,注意遍历的时候 end 需要取 max | |
动态规划 | ||
70. 爬楼梯 | 注意循环的结束条件 | $f(n) = f(n-1) + f(n-2)$ |
118. 杨辉三角 | 创建模拟逐层填充 | |
198. 打家劫舍 | dp[i] 表示偷窃到 i 号房屋所获取的最高金额 |
$dp[i]=max(dp[i−1],nums[i]+dp[i−2])$ |
279. 完全平方数 | dp[i] 表示数字 i 最少可以由多少个完全平方数组成 |
$dp[i]=min(dp[i],dp[i−j^2]+1)$ |
322. 零钱兑换 | dp[i] 表示凑成金额 i 所需的最少硬币数 |
$dp[i]=min(dp[i],dp[i−coin]+1)$ |
139. 单词拆分 | dp[i] 表示字符串 s 的前 i 个字符是否可以用字典中的单词拼接出来 |
$dp[i] = dp[j]$ && $s[j:i] ∈ wordDict$ |
300. 最长递增子序列 | dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度 |
$dp[i]=max(dp[i],dp[j]+1)$ |
152. 乘积最大子数组 | 通过维护 maxProd 和 minProd 同时处理了包含当前元素的最大子数组乘积和最小子数组乘积的情况,而不需要回溯或进行额外的遍历(一次遍历即可) |
|
416. 分割等和子集 | 0/1 背包问题(先物后背,背倒序),dp[i] 表示是否能找到和为 i 的子集 |
$dp[i] = dp[i]$ |
32. 最长有效括号 | 用栈来做更方便,先把 -1 压入栈作为哨兵 |
|
多维动态规划 | ||
62. 不同路径 | dp[i][j] 表示从左上角 (0, 0) 走到位置 (i, j) 的路径数 |
$dp[i][j]=dp[i−1][j]+dp[i][j−1]$ |
64. 最小路径和 | dp[i][j] 表示从左上角 (0, 0) 到达位置 (i, j) 的最小路径和 |
$grid[i][j]+min(dp[i−1][j],dp[i][j−1])$ |
5. 最长回文子串 | dp[i][j] 表示子串 s[i:j+1] 是否是回文 |
$dp[i][j]=(s[i]==s[j])∧dp[i+1][j−1]$ |
1143. 最长公共子序列 | dp[i][j] 表示字符串 text1 的前 i 个字符和 text2 的前 j 个字符的最长公共子序列的长度 |
$dp[i][j]=dp[i−1][j−1]+1$$dp[i][j]=max(dp[i−1][j],dp[i][j−1])$ |
72. 编辑距离 | dp[i][j] 表示将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最少操作数 |
|
技巧 | ||
136. 只出现一次的数字 | 全部元素的异或运算结果即为数组中只出现一次的数字 | |
169. 多数元素 | 摩尔投票法,不通的元素数量 – 抵消、相同的 ++ | $O(1)$ 空间 |
75. 颜色分类 | 分别遍历判断交换元素,确定 0 和 1 的位置 | |
31. 下一个排列 | ||
287. 寻找重复数 | 数字都在 [1, n] 范围内,快慢指针,即 142. 环形链表 II |
标准 I/O
fmt.Scan()
、fmt.Scanln()
、bufio.NewScanner(os.Stdin)
多组输入、大批量输请使用
bufio.Reader
来替换fmt.Scan
❗️ 不然可能会出现超时
输入: 包括两个正整数 a, b (1 <= a, b <= 10^9),输入数据包括多组
输出: a+b 的结果
1 | // 组数不确定,组内元素数量确定,无结束输入条件 |
输入: 第一行包括一个数据组数 t (1 <= t <= 100),接下来每行包括两个正整数 a, b (1 <= a, b <= 10^9)
输出: a+b 的结果
1 | // 组数确定,组内元素数量确定 |
输入: 包括两个正整数 a, b (1 <= a, b <= 10^9),输入数据有多组,如果输入为 0 0 则结束输入
输出: a+b 的结果
1 | // 组数不确定,组内元素确定,有结束输入条件 |
输入: 输入数据包括多组。每组数据一行,每行的第一个整数为整数的个数 n (1 <= n <= 100),n 为 0 的时候结束输入。接下来 n 个正整数,即需要求和的每个正整数。
输出: 每组数据输出求和的结果
1 | // 组数不确定,组内元素数量确定并用数组装,有结束输入条件 |
输入: 第一行包括一个正整数 t (1 <= t <= 100),表示数据组数。接下来 t 行, 每行一组数据。每行的第一个整数为整数的个数 n (1 <= n <= 100)。接下来 n 个正整数,即需要求和的每个正整数。
输出: 每组数据输出求和的结果
1 | // 组数确定,组内元素数量确定 |
输入: 输入数据有多组,每行表示一组输入数据。每行的第一个整数为整数的个数 n(1 <= n <= 100)。接下来 n 个正整数, 即需要求和的每个正整数。
输出: 每组数据输出求和的结果
1 | // 组数不确定,组内元素数量确定并用数组装,无结束输入条件 |
输入: 输入数据有多组,每行表示一组输入数据。每行不定有 n (1 <= n <= 100) 个整数,空格隔开。
输出: 每组数据输出求和的结果
1 | // 组数不确定,组内元素数量不确定⚠️,无结束输入条件 |
输入: 输入有两行,第一行 n,第二行是 n 个空格隔开的字符串
输出: 输出一行排序后的字符串,空格隔开,无结尾空格
1 | // 组数确定,组内元素数量确定 |
输入: 多个测试用例,每个测试用例一行。每行通过空格隔开,有 n 个字符串,n<100
输出: 对于每组测试用例,输出一行排序过的字符串,每个字符串通过空格隔开
1 | // 组数不确定,组内元素数量不确定⚠️ |
输入: 多个测试用例,每个测试用例一行。每行通过 ,
隔开,有 n 个字符串,n<100
输出: 对于每组用例输出一行排序后的字符串,用 ,
隔开,无结尾空格
1 | // 和上面那个一样的 |
构造二叉树
1 | package main |
武器库
标准库容器
- slice(切片)
- map(散列表)
- list(双向链表)
- heap(堆)
- ring(环)
排序函数
1 | import "sort" |
反转切片
1 | func ReverseSlice(slice []int) { |
使用 Map 计数
1 | // Example of using map for counting occurrences of elements in a slice. |
检查 Map 中是否存在某个元素
1 | // Example of using map for checking if an element exists. |
栈的 Push 和 Pop
1 | package main |
常见排序
冒泡
1 | func bubbleSort(nums []int) []int { |
快速
1 | /* 快速排序 */ |
华为笔试回顾
2024.05.22 实习笔试沿用到秋招
2024.10.25 面试没问到笔试复盘
题目:给定两个链表,获取两者中相同节点值的 最大连续片段。如果没有公共片段,返回 -1
解答要求:时间限制: C/C++ 1000ms, 其他语言: 2000ms 内存限制: C/C++ 256MB, 其他语言: 512MB
输入:第一行表示链表 1,第二行表示链表 2,每条链表长度不超过 20 个元素,链表不会为空
输出:公共链表片段
1 | // 查找两个链表中最长的公共连续片段 |
题目:露天矿采矿作业的特点是规模大,矿石和废料的移动量达到百万吨,运输成本开销较大,需要寻求一种最优的运输路径节省成本。
已知矿场可以划分成 N*M 的网格图,每个网格存在地形的差异,因此通过不同网格时,成本开销存在差异
- 标志为’S’的网格为运输起点;
- 标志为’E”的网格时运输终点;
- 标志为’B’的网格为阻塞点,不允许通行;
- 标志为’C’的网格为检查点,矿车在运输路径中,至少需要进入一次检查点。
- 标志为‘数字”的网格,其数字表示经过该网格的成本开销。
运输矿车只能上下左右 4 个方向运行,不允许斜对角进入其他网格。必要时可重复进入网格,请根据输入的网格图,寻求一条从 S 网格到 E 网格,并且至少经过一次检查点的最低成本运输路径,并输出其成本开销
解答要求:时间限制: C/C++ 1000ms, 其他语言: 2000ms 内存限制: C/C++ 256MB, 其他语言: 512MB
输入:
- 第一行:网格图的行数 N [3,200] 和网格图的列数 M [3,200],使用空格隔开。
- 第二行至第 N+1 行:网格图每一行的元素,可以为’S’,E’,’B’,‘C’或者数字 [0,100],并且有且仅有一个’S’和一个’E’,同时存在一个或者多个‘C’,并依次使用空格隔开。
输出:第一行:输出运输最低成本开销。如果不存在可达通路,请输出-1
1 | package main |