日常leetcode代码思路总结(持续更新)
动态规划思路
- 递归方式暴力求解(基本不能满足时间要求)
- 递归方式 + 剪枝(勉强可以达到时间要求)
- 动态规划求解
a. 确认dp含义,即定义function
b. 递推公式,即f(x)与f(x-k)关系,或者f(x,y)和(x-k, y-k)关系
c. 确认初始化值
d. 循环遍历
leetcode & 解题思路
难易 | leecode题号 | 题目描述 | 思路 |
---|---|---|---|
简单 | 121. 买卖股票的最佳时机 | 只准一次买卖 | 0表示持有,1表示不持有; dp[0][i] = max(dp[0][i-1], -prices[i]); dp[1][i] = max(dp[1][i-1], dp[0][i] + prices[i]) |
中等 | 122. 买卖股票的最佳时机 II | 无限买卖 | indexMin表示极小值下标,indexMax表示极大值下标,在极大值点切换时, 累加每次indexMax > indexMin -> prices[indexMax]-prices[indexMin],额外需考虑最终一直上涨情况 |
困难 | 123. 买卖股票的最佳时机 III | 两次买卖 | dp[0][0] = 0, dp[0][1] = -prices[0], dp[0][2] = 0, dp[0][3] = -prices[0]; dp[i][1] = max(dp[i-1][1], dp[i][0] - prices[i]); // 第一次买 dp[i][2] = max(dp[i-1][2], dp[i][1] + prices[i]); // 第一次卖 dp[i][3] = max(dp[i-1][3], dp[i][2] - prices[i]); // 第二次买 dp[i][4] = max(dp[i-1][4], dp[i][3] + prices[i]); // 第二次卖 |
困难 | 188. 买卖股票的最佳时机 IV | k次买卖 | 由2次买卖找规律推导出来,k = k * 2,通过奇偶进行[0, n), [0, k)双层遍历 |
困难 | 309. 买卖股票的最佳时机含冷冻期 | 不限制买卖次数,有一天冷静期 | f[i[[0]表示持有, f[i][1]表示不持有,处于冷静期, f[i][2]表示不持有,不处于冷静期 f[i][0] = max(f[i-1][2]-prices[i], f[i-1][0]); f[i][1] = f[i-1][0] + prices[i]; f[i][2] = max(f[i-1][2], f[i-1][1]); |
中等 | 5. 最长回文子串 | 字符串回文子串最长长度 | s[i,j]为回文串, dp[i][j] = true (i == j); dp[i][j] = s[i] == s[j] && (i+1 > j-1 || dp[i+1][j-1]) |
中等 | 131. 分割回文串 | 返回字符串分割成的所有回文子串 | s[i,j]为回文串, dp[i][j] = true (i == j); dp[i][j] = s[i] == s[j] && (i+1 > j-1 || dp[i+1][j-1]) dfs(s, index, dp): 深度遍历 |
困难 | 132. 分割回文串 II | 返回字符串分割成的所有回文子串中切割次数最少 | s[i,j]为回文串, dp[i][j] = true (i == j); dp[i][j] = s[i] == s[j] && (i+1 > j-1 || dp[i+1][j-1]) f[j] = min(f[j], i == 0 ? 1 : f[i-1]+1) |
困难 | 10. 正则表达式匹配 | '.'和‘*’正则匹配 | f(i,j) = f(i-1, j-1) s[i-1] = p[j-1] || p[j-1] = ‘.’ f(i,j) = f(i, j-2) | f(i-1, j) p[j-1] = ‘*’ && (s[i-1] = p[j-2] || p[j-2] = ‘.’) f(i,j) = f(i, j-2) p[j-1] = ‘*’ && (s[i-1] != p[j-2] && p[j-2] != ‘.’) 注意初始化i=0层 |
困难 | 44. 通配符匹配 | '?'和‘*’通配匹配 | f(i,j) = f(i-1, j-1) s[i-1] = p[j-1] || p[j-1] = ‘?’ f(i,j) = f(i, j-1) | f(i-1, j) p[j-1] = ‘*’ 注意初始化i=0层 |
困难 | 22. 括号生成 | 输入n,输出所有可匹配的n对括号 | 对于满足括号匹配的字符串必须满足下述两个条件:1)该字符串的前缀字符串“(”的个数>=“)”的个数;2)字符串“(”的个数=“)”的个数 |
困难 | 32. 最长有效括号 | 输出长度 | f(n)表示第n个字符‘)’对应前缀串中最长可达的‘(’下标,初始化:f(i) = i-f(dp[i]-1) + 1, dp[i] != -1 && f(dp[i]-1) != -1。 |
中等 | 678. 有效的括号字符串 | 输出是否完全括号匹配 | 包含字符’(‘, ‘)’, ‘*’。 定义两个stack,dp 和 star,分别用于存储’(‘和’*‘的下标,dp遇到’)‘出栈,star用于无’(‘时,位于’)'前匹配出栈,最后在遍历dp和star中剩余的,dp中下标在star下标前;最后返回star.empty()。 |
困难 | 42. 接雨水 | 输出雨水的体积 | left(n), right(n)分别表示从左到右和从右到左可见的最高柱子, f(n)=left(n)-height(n) > 0 && right(n)-height(n) > 0 ? min(left(n, right(n)) - height(n), 最后返回sum(f(n))。 |
困难 | 85. 最大矩形 | 输出连续的最大矩形的体积 | 分两阶段计算, 第一阶段计算dp(i, j),表示以i,j为底的柱子高度, 第二阶段计算以dp(i,j)为高height的j列左右可延伸的最大宽度weight,返回值ans = max(ans, height*weight)。 考虑边界问题可在前后分别增加两个哨兵。 |
困难 | 84. 柱状图中最大的矩形 | 输出连的柱子组层的最大矩形的体积 | 计算以dp(i)为高height的j列左右可延伸的最大宽度weight,返回值ans = max(ans, height*weight)。 考虑边界问题可在前后分别增加两个哨兵。 |
中等 | 221. 最大正方形 | 输出最大正方形的体积 | f(i,j) 表示以i,j为终点的正方形的边长, f(i, j) = min(f(i-1, j), f(i, j-1), f(i-1, j-1)) + 1, matrix(i, j) = ‘1’, 返回ans = max(ans, f(i, j) * f(i, j)) |
困难 | 887. 鸡蛋掉落 | k个鸡蛋n层,第x层及以上鸡蛋碎,求最小使用次数 | f(k, n) = min(max(f(k-1, n-x), f(k, x-1), 第x层鸡蛋碎 or 没碎。 |
中等 | 337. 打家劫舍 III | 二叉树路径,相邻两个房间不能同时打劫 | f(root) = max(root->val+f(root->left->left)+f(root->left->right)+f(root->right->left)+f(root->right->right), f(root->left)+f(root->right)),再加上缓存。 |
困难 | 329. 矩阵中的最长递增路径 | 从矩阵任一点出发,上下左右移动 | f(p,q,v) = max(f(p,q,v), f(p+direct[x],q+direct[y],v)+1), ans = max(ans, f(p,q,v),其中,p表示横轴,q表示纵轴,v表示取值,x,y表示上下左右 |
困难 | 354. 俄罗斯套娃信封问题 | 二维整数数组 e n v e l o p e s [ i ] = [ w i , h i ] envelopes[i] = [w_i, h_i] envelopes[i]=[wi,hi] | sort(envelopes, cmp),按照wi升序,wi相同按照hi降序,dp存储hi值,通过lower_bound()对dp插入hi,最后返回dp.size() |
困难 | 403. 青蛙过河 | 青蛙过河,只能向前跳当前步数的[k-1, k, k+1]步 | dp[i][k] = dp[j][k-1] || dp[j][k] || dp[j][k+1], i:[1,n], j:[i-1:0], k=stones[i] - stones[j], k > j+1, 返回结果:i == n-1 && dp[i][k] |
困难 | 410. 分割数组的最大值 | 数组nums进行k次切割使得切割后的连续子序列和最大值最小 | dp[i][j]表示对于数组前i个数j次切割,切割后的连续子序列和最大值最小的值,dp[i][j]=min(dp[i][j], max(dp[p-1][j], sum(sub(p+1, i))), 初始化dp[0][0] = 0, dp[i][j] = LLONG_MAX |
中等 | 413. 等差数列划分 | 数组nums中满足等差数列的连续子序列个数 | f(i,j)表示以nums[i]为头满足等差数列的最长连续子序列且j-i+1 > 3,再遍历到j,依次遍历下去,将长度存储到res数组中,最后sum((res[k]-2)*(res[k]-1)/2) |
困难 | 446. 等差数列划分 II - 子序列 | 数组nums中满足等差数列的子序列个数 | f[i][d]表示以i为尾等差为d的序列中序列的个数。f[j][d] += d是否已经存在 ? f[j][d]+1 : 0; ans += f[j][d]; f[i][d] += f[j][d] |
中等 | 435. 无重叠区间 | 没有重叠的序列,求删除数组个数 | 序列intervals,sort(intervals),ans++, intervals[i][0] == intervals[i-1][0] |
中等 | LCR 131. 砍竹子 I | 将竹子砍成k段,使得每段竹子乘积最大 | dp[i] = max(dp[i], max((i-j) * dp[j], dp[i-j] * j)) |
中等 | LCR 016. 无重复字符的最长子串 | 找出不含有重复字符的 最长连续子字符串 的长度 | unordered_map<char, int> dp, key表示s的字符,value表示出现次数 |
困难 | LCR 017. 最小覆盖子串 | 给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。 | 滑动窗口,unordered_map<char, int> cnt, ori, key表示字符串的字符,value表示出现次数,ori对t的统计,cnt对命中s的统计。 |
困难 | 239. 滑动窗口最大值 | 返回 滑动窗口中的最大值 | 滑动窗口,deque 双端队列,队列中存储下标。 |
困难 | 466. 统计重复个数 | 统计<s2,n2>在<s1,n1>中重复的个数 | hash + 下标检索,unordered_map<int, pair<int, int>> dp, key: index, pair<int, int> pre, pair<int, int> diff,pre存储产生重复时{s1cnt, s2cnt},diff存储{curs1cnt-s1cnt, curs2cnt-s2cnt}。ans = pre.second + (n1-pre.first)/diff.first * diff.second; rest = (n1-pre.first)%diff.first, 在rest中进行index下标匹配,return ans/n2 |
中等 | 146. LRU 缓存 | LRU缓存 | 双向队列+map |
中等 | 208. 实现 Trie (前缀树) | 前缀树,实现insert, search能力 | Trie, vector<Trie *> children[26], isEnd. |
简单 | 1047. 删除字符串中的所有相邻重复项 | 删除重复项 | 栈 |
中等 | 1209. 删除字符串中的所有相邻重复项 II | 删除指定k个重复项 | vector<pair<char, int>> dp; |
中等 | 416. 分割等和子集 | 分割数组相等两部分 | 思路参考0-1背包,1)判断sum为2的倍数 & maxNum < sum/2,2)通过0-1背包思路解答,dp[i][j],其中,i为选择数据的个数,j为目标值;dp[i[[j] = dp[i-1][j] | dp[i-1][j-nums[i]] (j >= nums[i]), dp[i-1][j] (j < nums[i]);return dp[n][sum/2] |
中等 | 1049. 最后一块石头的重量 II | 选择任意两个石头相撞,最终得到剩余重量 | 思路0-1背包 |
中等 | 494. 目标和 | 输出count | positive_sum - negative_sum = target, positive_sum = (sum-target)/2, 思路0-1背包 |
中等 | 322. 零钱兑换 | 输出最小count | 完全背包,组合问题,先遍历物品再遍历背包–组合问题,先遍历背包再遍历物品–排列问题;for循环倒序–物品不可重复,for循环正序,物品可重复。 |
中等 | 518. 零钱兑换 II | 输出count | 完全背包,组合问题,先遍历物品再遍历背包–组合问题,先遍历背包再遍历物品–排列问题;for循环倒序–物品不可重复,for循环正序,物品可重复。 |
中等 | 377. 组合总和 Ⅳ | 输出count | 完全背包,排列问题,先遍历物品再遍历背包–组合问题,先遍历背包再遍历物品–排列问题;for循环倒序–物品不可重复,for循环正序,物品可重复。 |
中等 | 279. 完全平方数 | 输出最少count | 完全背包,组合问题,先遍历物品再遍历背包–组合问题,先遍历背包再遍历物品–排列问题;for循环倒序–物品不可重复,for循环正序,物品可重复。 |
中等 | 139. 单词拆分 | 输出是否命中 | 完全背包,排列问题,先遍历物品再遍历背包–组合问题,先遍历背包再遍历物品–排列问题;for循环倒序–物品不可重复,for循环正序,物品可重复。 |
困难 | 2551. 将珠子放入背包中 | k个背包,切割数组 | 通过数学思维解答:链接 |
中等 | 467. 环绕字符串中唯一的子字符串 | 输出子串个数 | vector dp(26),遍历字符串s, dp[s[i]-‘a’] = max(dp[s[i]-‘a’, k),k表示以b结尾的子串个数。 |
中等 | 199. 二叉树的右视图 | 二叉树右视图 | 二叉树层次遍历 |
中等 | 79. 单词搜索 | 给定单词在矩阵中检索 | 图的深度遍历dfs |
中等 | 212. 单词搜索 II | 给定单词组在矩阵中检索 | 图的深度遍历dfs + 前缀树 |
困难 | 312. 戳气球 | 戳气球的分 | 图的深度遍历dfs,类似快排思想,f[left,right] = max(f[left, right], f(i) * f(left-1) * f(right+1) + f[left, i-1] + f[i+1, right])。f(i)表示以i为结束点的[left, right]区间内最大结果。 |
leetcode 热点100题
难易 | leecode题号 | 题目描述 | 思路 |
---|---|---|---|
中等 | 128. 最长连续序列 | 返回连续递增序列个数 | 通过unordered_set存储数据,再遍历unordered_set。 |
困难 | 76. 最小覆盖子串 | 返回最小子串 | 滑动窗口,定义两个unordered_map分别存储字符串s, t。两个指针left, right分别表示s的子串[left, right]校验是否包含t。 |
困难 | 41. 缺失的第一个正数 | 返回不包含的最小正整数 | 置换。如果0<x<=n,那么一定可以nums[x-1] = x,即nums[nums[i]-1] = nums[i]。 |
困难 | 25. K 个一组翻转链表 | 每满足k个翻转一次 | 链表翻转,注意链表的最后不满足k的末尾不反转。 |
困难 | 23. 合并 K 个升序链表 | k个升序链表归并 | 优先队列,小顶堆。 |
困难 | 124. 二叉树中的最大路径和 | 二叉树最大路径(路径可以为任何节点) | dfs,返回值是node到left或者node到right上最大值。定义全局变量ans。 |
中等 | 236. 二叉树的最近公共祖先 | 最近公共祖先 | (flagL && flagR) || ((flagL || flagR) && (root == p || root == q)) |
中等 | 437. 路径总和 III | 不一定要叶子节点 | int dfs(root->left, targetSum - root->val) |
中等 | 113. 路径总和 II | 从根节点到叶子节点 | void dfs(root->left, targetSum - root->val, line) |
简单 | 112. 路径总和 | 从根节点到叶子节点 | void dfs(root->left, targetSum - root->val) |
中等 | 105. 从前序与中序遍历序列构造二叉树 | 构建二叉树 | root->left = dfs(preorder, pBegin+1, pBegin+leftL, inorder, iBegin, iBegin+leftL-1); root->right = dfs(preorder, pBegin+leftL+1, pEnd, inorder, iBegin+leftL+1, iEnd); |
中等 | 114. 二叉树展开为链表 | 链表 | 将左子树逐步插入右子树 |
中等 | 98. 验证二叉搜索树 | 二叉搜索树 | dfs(root->left, root->val, lower) && dfs(root->right, upper, root->val) |
困难 | 4. 寻找两个正序数组的中位数 | 正序列去中位数 | 二分查找,nums1的前(k/2-1)个数或者nums2的前(k/2-1)个数可以直接在循环中去掉。 |
困难 | 295. 数据流的中位数 | 定义一个数据流类返回指定任意位置的中位数 | 优先队列,定义两个优先队列,第一个为大顶堆(less),第二个为小顶堆(greater),在数据不断插入过程中始终保持大顶堆.size - 1 = 小顶堆.size or 大顶堆.size = 小顶堆.size。 |
中等 | 1143. 最长公共子序列 | 返回最长公共子序列 | dp[i][j] = dp[i-1][j-1], text1[i]==text2[j] ->= max(dp[i-1][j], dp[i][j-1]), text1[i] != text2[j] |
中等 | 72. 编辑距离 | 两个字符串之间距离,将字符串a转换成字符串b最小步数 | a[i] != b[j]时,a插入等同于b删除,a删除等同于b插入,a修改为b等同于b修改为a;因此有三种情况:1)a删除,2)b删除,3)a修改为b。 dp[i][j] = dp[i-1][j-1], a[i]==b[j] -> =min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1]))+1 |
中等 | 152. 乘积最大子数组 | 连续乘积子数组 | preMax = max(preMin*nums[i], preMax*nums[i], nums[i]) preMin = min(preMin*nums[i], preMax*nums[i], nums[i]), ans = max(ans, preMax) |
中等 | 53. 最大子数组和 | 连续累加子数组 | preMax = max(nums[i], preMax+nums[i]) ans = max(ans, preMax) preMax表示以当前i结尾的子数组max值。 |
中等 | 287. 寻找重复数 | n+1个数,取值[1:n] | 把数交换到原本的位置,如果已经有数在这个位置了,它就是重复的数 |
中等 | 75. 颜色分类 | 0,1,2 | 双指针,一个p0, 一个p1, nums[i] == 0时,p0++, p1++; nums[i] == 1时,p1++ |
中等 | 394. 字符串解码 | 数字[字符] | 递归,getString: number[str], return ret + getString() |
中等 | 560. 和为 K 的子数组 | map + 前缀子数组和。子数组为连续的 | pre[i]表示nums[0,i]的和,pre[i] = pre[i-1] + nums[i], -> pre[i] = pre[j-1] + nums[j,i], 可以假设nums[j, i] == k -> pre[j-1] = pre[i] - k |
中等 | 763. 划分字母区间 | 同一个字母必须在一个片段,求最小多片段切片 |
e
n
d
=
m
a
x
(
e
n
d
,
e
n
d
c
)
end = max(end, end_c)
end=max(end,endc) res.push(end-start+1), start = end+1, if (i == end) |
中等 | 55. 跳跃游戏 | 数组值为最远跳跃长度 | right = max(right, i+nums[i]), if(i <= right), return right >= n-1 |
中等 | 45. 跳跃游戏 II | 一定可达终点,求最少跳跃次数 | while(i < len-1) { maxPos = max(maxPos, i+nums[i]); if (index == end) {end = maxPos; ans++;} } |
中等 | 54. 螺旋矩阵 | 旋转输出 | 模拟,通过direct[4][2]模拟旋转输出 |
中等 | 48. 旋转图像 | 矩阵顺时针旋转90度 | 水平翻转+对角线翻转 |
中等 | 148. 排序链表 | 链表排序 | 归并排序,找到mid,然后递归两两排序 |
中等 | LCR 170. 交易逆序对的总数 | 逆序对个数 | 归并排序,在比较时候计算个数。 |
中等 | 134. 加油站 | 环状加油站,输出是否能走完 | [i,j]走不到->[i+1, j]也走不到 |
困难 | 135. 分发糖果 | 相邻两个节点分数越高分发糖果越多 | left[n], right[n] |