08 2021 档案
摘要:dp[i][j][k], 前 i 个数, 完成 j 笔交易, k: 0, 不持股, k: 1 持股....状态转移方程如下: dp[i][j][0] = dp[i - 1][j][0]; if(j > 0) dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j - 1
阅读全文
摘要:贪心: 对于开始时间相同的会议来说,肯定是先安排结束时间早的。 所以,可以枚举时间点,当某个会议在此时间点开始了,则将其结束时间加入优先队列。 对于优先队列来说,如果结束时间小于当前时间点,那么将其弹出。否则,取 top,安排会议,即 ++ ans; // 由于需要从小到大排列结束时间,下列 pus
阅读全文
摘要:这个题很重要的一点,null 节点被表示为 # 号,所以每遇到两个连续的 # 时,就表示这是一个叶子节点。 这个时候就需要用栈来进行维护,当遇到连续两个 #,就将这两个 # 弹出,并弹出这个叶子节点,随后再 push 一个 #,表示这个点已经被删掉。 如样例 9 3 4 # # 1 # # 2 #
阅读全文
摘要:假设根节点处深度为 1. ①:层序遍历,存储。对所有深度为偶数的进行一次反转。 ②:用两个双端队列 deq1,deq2. 当在奇数层进行 push 的时候,先 push_front 左儿子,再 push_front 右儿子;偶数层则与其相反 (push_front 进 deq2)。。当 deq1 为
阅读全文
摘要:看代码: class Solution { public: bool isRectangleOverlap(vector<int>& rec1, vector<int>& rec2) { int x1 = max(rec1[0], rec2[0]), y1 = max(rec1[1], rec2[1
阅读全文
摘要:①:马拉车的 p 数组,即改造后的字符串,以 i 为中心的最大回文串的长度 p[i],ret += (p[i] / 2); ②:dp[i][j] 表示 i 到 j 是否为回文,dp[i][j] = dp[i + 1][j - 1] & (s[i] == s[j]),为 true 则 ++ret; c
阅读全文
摘要:由于 left 的值可能为 1,所以用一个虚拟节点,其 next 之前 head,来作为一个哑结点。接下来只需要找到 left 和 right 位置的节点,然后进行反转即可。 class Solution { public: ListNode* reverseBetween(ListNode* he
阅读全文
摘要:用两个栈,分别为 stk1,stk2,当 push 的时候,直接 push 进 stk1,pop 时,如果 stk2 为空,则将 stk1 全部弹出并依次入栈 stk2,返回 stk2 的 top 即可。 class MyQueue { public: /** Initialize your dat
阅读全文
摘要:其实就是一个数学问题,每一次可以算出当前用第几个数,对于每个长度来说,第一个数的每一个数后面引导着 fac[n - 1] 个排列。 比如输入为 3,5,使用的第一个数为 dig = (5 + fac[2] - 1) / fac[2],即第 3 大的数 (所以需要记录一下哪些数用过了)。 然后直接减去
阅读全文
摘要:① dp[i][j] 表示前 i 个数, 分成 j 份的各自和的最小值。 那么 dp[i][j] = min(dp[k][j - 1], pre[i] - pre[k - 1]), 其中 k ∈ [j - 2, i - 1], (前 k 个数至少保证能组成 j - 1 份) ② 二分答案,check
阅读全文
摘要:用 dfs 暴力搜索即可通过本题。即如下代码: class Solution { public: int kSimilarity(string s1, string s2) { solve(s1, s2, 0, 0); return ret; } private: int ret = INT_MAX
阅读全文
摘要:如果 s.size() > 12 或者 < 4, 直接 return, 这个无话可说.. 然后就是枚举三个点的位置, 判断每个部分是否含前缀 0 和是否小于 255 即可。 如,第一部分只可能是 下标为 i ∈ [0, 2],然后第二部分则为 j ∈ [i + 1, i + 3],第四部分为 k ∈
阅读全文
摘要:题目太长了:https://leetcode-cn.com/problems/remove-comments/ 简单意思就是:给 vector<string> &source,表示每一行的代码,代码中有 // 注释和 /* */ 的注释,返回删除后的 vector<string> 然后题目保证: 每
阅读全文
摘要:对每个字母,都有一个最左边界与最右边界,答案就是这些边界区间进行合并。 class Solution { public: vector<int> partitionLabels(const string &s) { vector<pair<int, int>> idx(26, {INT_MAX, I
阅读全文
摘要:题目:https://leetcode-cn.com/problems/regular-expression-matching/ 动态规划:dp[i][j] 表示 s 串前 i 个 与 p 串前 j 个是否能够成功匹配。 状态转移方程不难: if(s[i] == p[j] || p[j] == '.
阅读全文
摘要:查询区间最大值,解法挺多的:线段树,RMQ 都可。不过都是 O(nlogn) 单调队列:O(n),注意 i >= k - 1 才加入答案,以及 pop_front 的条件是 .front() <= i - k class Solution { public: vector<int> maxSlidi
阅读全文
摘要:对每个高度,它能构成的最大矩形就是,一直向两侧扩展,直到某个高度比其小,停止 单调栈,一次 for 循环就可以求出左右两边比其小的数的下标。 class Solution { public: int largestRectangleArea(vector<int> &heights) { stack
阅读全文
摘要:链接:https://www.cnblogs.com/czsharecode/p/9705358.html mx 表示回文串所能达到的最右边界, mid 表示这个能达到最右边界的回文串的中心点。 关键点:p[i] = mx > i ? min(mx - i, p[2 * mid - i]) : 1
阅读全文
摘要:① 这个题不要想着去求最后接出来的雨水长啥样,然后再来算面积。就比如示例,index 1 与 index 3 组成一个坑,可以求得一个面积,index 3 和 index 7 又组成一个坑 ......。这样子不是太好写,虽然也可以:用单调栈求出 index i 右边第一个大于等于它的数的下标 bi
阅读全文
摘要:①:将成功匹配的括号进行标记,最后剩下的没有被标记的括号就是未成功匹配的。所以答案就是这个未被标记的括号的距离最大值。时间 O(n),空间 O(n) ②:括号不合法只有两种情况,')' 多了,或者 '(' 多了。所以从前往后遍历时,如果 ')' 数量比 '(' 多,那么这个点就是一个分割点。而由于从
阅读全文
摘要:首先需要明确,每个节点最后的值为:右子树的和 + 自身的值 + 父亲一侧的所有节点和。 所以用一个 base 变量来标明父亲一侧的和是多少即可。 class Solution { public: TreeNode* convertBST(TreeNode* root) { auto _ = solv
阅读全文
摘要:贪心:可以通过确定左右边界来获得答案。 确定左边界:如果一个下标 i,满足 nums[i] = min(nums[i], ... nums[n - 1]),那么这个 i 不然不在答案里,所以 ++ i 即可。 确定右边界:同理,如果一个下标 i,满足 nums[i] = max(nums[0], .
阅读全文
摘要:两题如出一辙:https://www.cnblogs.com/rookie-acmer/p/15164840.html class Solution { public: int subarraySum(const vector<int>& nums, const int &k) { unordere
阅读全文
摘要:① 可以直接 dfs + 一些小的剪枝来做; ② dp[i][j] 表示前 i 个数都使用,和为 j 的方案数。由于和可能为负,但 sum(num[i]) 最大值为 1000,所以可以开 2000 大小的数组,并做一个偏移量来解决负数和问题; ③ 假设添加符号的数的和为 a,那么另一半数的和为 su
阅读全文
摘要:前缀和是可以的,时间 O(n * 26),空间 O(n * 26)。然后其实可以优化,不需要前缀和,因为长度固定,每次只需要更改一个字母的次数。时间 O(n * 26),空间 O(26 * 2) hash:将 a 当做 26 的 0 次方,b 当做 26 的 1 次方,即转换为 26 进制。时间 O
阅读全文
摘要:可以转换成 01 背包,容量是 sum / 2,每个物品的价值是其值本身 class Solution { public: bool canPartition(vector<int>& nums) { int sum = 0; for(int i = 0; i < nums.size(); ++ i
阅读全文
摘要:dp[i][0] 表示到第 i 天不持股的最大利润,dp[i][1] 表示到第 i 天持股的最大利润,dp[i][2] 表示到第 i 天处于冷冻期的最大利润,表示第 i + 1 天不能买入。 初始化的时候整体初始化为 -INF 即可,持股即买下这一天的股票,直接减去 price[i] 即可。 cla
阅读全文
摘要:题目: https://leetcode-cn.com/problems/lfu-cache/ 主要是存储思路吧: hash 映射次数与该次数对应的 list 结点集合,并且还有一个 hash 映射 key 与其对应结点所在的位置 然后就是维护最小次数 minTimes,每当当前结点次数+1,放到其
阅读全文
摘要:问题可以替换为,一个数组,求其有多少个子数组和为 target,两者方法是一样的。 对于数组,可以 hash 存储前缀和,若当前前缀和是 preSum,那么到此 index 为止,ans += sum[preSum - target],这是很显然的。 对树上的,有一个树上前缀和,sum[i] 表示
阅读全文
摘要:用栈来存储字符,每一次遇到 ']' 时,就弹栈到栈顶为 '[' 为止,这样就得到了中括号内的字符,然后继续弹,得到次数即可。 最后按照次数与字符串在入栈 class Solution { public: string decodeString(const string &s) { stack<cha
阅读全文
摘要:尺取:在当前满足条件的情况下,移动左指针,再次判断是否满足条件即可。然后取整体答案的最小值。 满足条件的 check:记录每个字母用的次数,然后依次比较。 class Solution { public: string minWindow(const string &s, const string
阅读全文
摘要:题目例子图片比较多,贴题目链接: https://leetcode-cn.com/problems/reverse-nodes-in-k-group/ 思路:每 k 个一组将链表划分开,然后进行翻转链表,翻转完成后,再用上一次翻转的尾结点连接上。 第一次翻转时由于没有上一次翻转的尾结点,所以用一个虚
阅读全文
摘要:发现 coins.length 很小,而且 amount 最大为 1e4,所以完全背包 dp 即可。 像这个数一样采用 BFS 也行,但是很慢。https://leetcode-cn.com/problems/perfect-squares/ class Solution { public: int
阅读全文
摘要:hash 统计次数,在通过次数反向存储 出现了这么多次的数 是哪些。 最后对次数做堆处理,遍历 每个次数中的所有数,取前 k 个。可能看代码更清楚 class Solution { public: vector<int> topKFrequent(vector<int>& nums, int k)
阅读全文
摘要:就是之前的数组的 dp,放到的树上,思想没变; /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : va
阅读全文
摘要:dp[i] = dp[i >> 1] + (i & 1); class Solution { public: vector<int> countBits(int n) { vector<int> dp(n + 1); for(int i = 1; i <= n; ++ i) { if(i & 1)
阅读全文
摘要:先做这个,一样的思路:https://www.cnblogs.com/rookie-acmer/p/15101721.html class Solution { public: int findDuplicate(vector<int>& nums) { for(int i = 0; i < num
阅读全文
摘要:① dp:完全背包,没啥好说的 ② bfs:有很重要的一点,在 bfs 中的循环中提前判断是否到达 0,能够减少很多计算。是减少很多很多!!! ③数学题:不记录。 dp: class Solution { public: int numSquares(int n) { vector<int> dp(
阅读全文
摘要:有两种方法: ① 二分 + 前缀和。枚举每个点作为起点,二分正方形边长,利用前缀和判断当前的正方形是否全 1 即可。 ② 单调栈。有一个最大全 1 的矩形题目,而最大正方形肯定被最大矩形包含,假设最大矩形长宽为 a, b,那么每一次找到的一个当前行的最大矩形时,ans = max(ans, min(
阅读全文
摘要:拓扑。 存 prerequisties[1] 到 prerequisties[0] 的边,然后记录入度。 用队列来进行拓扑,边对应终点入度- 1,入度为 0 时,加入队列。 最后判断每个点是否入度为 0 即可。 class Solution { public: bool canFinish(int
阅读全文
摘要:对地图做一个 tag 标记,表示已经被遍历过。每当遇到一个为 1 且没有遍历过的岛屿,++ ans,然后 dfs 打 tag class Solution { public: int numIslands(vector<vector<char>>& grid) { if(grid.empty() |
阅读全文
摘要:dp[i][0] 表示到 i 位置,但是不包括最后一个 i 点的最大值。dp[i][1] 表示到 i 位置,但是包括最后一个 i 点的最大值。 dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]) dp[i][1] = dp[i - 1][0] + nums[i] 没
阅读全文
摘要:和子数组最大和没太大区别。 唯一的就是需要保存两个值,最大乘积和最小乘积,因为存在负数,最小乘积乘上这个负数就可能变最大乘积了. 不用滚动数组优化的代码: class Solution { public: int maxProduct(vector<int>& nums) { // dpMin[i]
阅读全文
摘要:dp,dp[i] 表示从 0 到下标 i 是否能够拆分 状态转移也不困难:假设当前 i 能够拆分,枚举 wordDict,如果 [i + 1, i + wordDict[j].size()] 与 wordDict[j] 相等,那么 dp[i + wordDict[j].size()] = true
阅读全文
摘要:hash,将每个出现的数都存上。 再一次枚举每一个数 x 作为起点,一直查询 x + i 是否存在。i 能够到达的最大值即是以 x 为起点的最大长度。 然后最重要的一点优化:当 x - 1 不存在时,才有必要枚举 x + i 查询,如果 x - 1 存在,那么它肯定不是最长的。所以最后的时间复杂度是
阅读全文
摘要:此题的关键部分就是如何处理 右儿子如何连接在左儿子上,然后再由左儿子变成父亲的右儿子。 现假设为一种最简单的情况: O (a) / \ O (b) O (c) 显然,节点 c 需要的得到的点是节点 b O (a) / \ O (b) O (c) \ O (d) 节点 c 需要的点是节点 d. 所以,
阅读全文
摘要:BFS,没啥好说的。 class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> ret; queue<pair<TreeNode*, int>> que; // Tree
阅读全文
摘要:递归验证,由于某个结点在父节点的左子树,但同时也可能作为祖父结点的右子树,这两个时候需要的最大最小值是不同的,所以需要返回 pair。然后按二叉搜索树性质验证即可。 class Solution { public: bool isValidBST(TreeNode* root) { auto _ =
阅读全文
摘要:z = x ^ y,计算 z 中二进制 1 的个数。。 z & (z - 1) 可以直接去除二进制最后一个 1 class Solution { public: int hammingDistance(int x, int y) { int z = x ^ y; int ret = 0; while
阅读全文
摘要:dfs 就行,枚举每个点作为起点进行搜索。剩下的就是剪枝的问题了 class Solution { public: bool exist(vector<vector<char>>& board, string word) { tag.resize(board.size()); for(auto &i
阅读全文
摘要:三路快排的 partition,将小于枢轴 tar 的数放左边,大于枢轴的数放右边,且中间全是枢轴值。即有三个下标 l, cur, r,其中 [0, r) 的值小于 tar,[l, r] 的值等于 tar,(r, nums.size) 大于 tar。 可以这样理解代码: 如果 nums[cur] <
阅读全文
摘要:排序区间,假设本区间为 now,下区间为 next,如果 next[0] >= now[1],则两区间可以合并。 然后就是一个细节问题,注意到就能 1A class Solution { public: vector<vector<int>> merge(vector<vector<int>>& i
阅读全文
摘要:dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) class Solution { public: int minPathSum(vector<vector<int>>& grid) { vector<vector<int>>dp(grid.size()); fo
阅读全文
摘要:本来是用数组标记当前位置时候能够达到,然后表示的从后往前标记,如果发现某个点被标记过了,那么前面点也肯定被标记了,就直接 continue。 然后发现,数组根本没有存在的必要,直接优化掉,维护可达到的最远位置即可。 //class Solution { //public: // bool canJu
阅读全文
摘要:hash:把 a 当做 26^0,b 当做 26^1,就这样。 或者,根本不hash成数字,直接当各个字母频次当成 string 处理 (频次就成了对应的字符,如 '9' + 1 再就是 ':')。 class Solution { public: vector<vector<string>> gr
阅读全文
摘要:向右旋转 90 度,即 先延 i = i 的对角线对折交换,在竖着对折交换 // 先延对角线交换, 再竖着按中间交换 class Solution { public: void rotate(vector<vector<int>>& matrix) { for(int i = 0; i < matr
阅读全文
摘要:完全背包。但是用搜索好像更好。 贴完全背包的代码 class Solution { public: vector<vector<int>> combinationSum(vector<int> &candidates, int target) { vector<vector<int>> ans[51
阅读全文
摘要:两遍二分就行 class Solution { public: vector<int> searchRange(vector<int> &nums, int target) { return {solve(nums, target, 0), solve(nums, target, 1)}; } pr
阅读全文
摘要:生成下一个排列的方法:从右往左找到第一个存在左边有比其小的数的两下标,假设分别为 l,r。 如:1 2 3 5 4 1 0,最后位置的 0 左边没有比他小的数,倒数第二个 1 左边没有比他小的数,4 左边比他小的数为 3,即 l = 2。 此时 l = 2 与 r = 4 交换,得到 1 2 4 5
阅读全文
摘要:递归生成括号。有两个关键参数 lCnt,rCnt 分别表示已生成的左括号和有括号的数量。 所以一共分出四种情况: ① lCnt == n && rCnt == n,将生成的括号放入 vector<string> ans 中 ② lCnt == n && rCnt < n,只能生成右括号 ③ lCnt
阅读全文
摘要:1:递归回溯的时候记录这是第几个结点,然后对应做删除即可。时间 O(n),空间 O(n) 2:双指针,slow 与 quick,让 quick 先走 n 步,然后再让 quick 与 slow 同时走,当 quick == nullptr 时,则 slow 停下的位置为倒数第 n 个结点。 对于法
阅读全文
摘要:先映射好数字与字母的关系,然后递归就行 class Solution { public: vector<string> letterCombinations(string &nums) { if(nums.empty()) return {}; init(); string temp; solve(
阅读全文
摘要:尺取。 class Solution { public: int lengthOfLongestSubstring(const string &s) { // 128 ASCII bool map[128] = {false}; int ans = 0, l = 0; for(int r = 0;
阅读全文
摘要:两棵树同时走,当遇到 root1 为 null 时,如果 root2 不为 null,就将 root2 拼接的 root1 的父节点上即可。同时不为 null 就是合并 val class Solution { public: TreeNode* mergeTrees(TreeNode* root1
阅读全文
摘要:这两个题其实挺像的:https://www.cnblogs.com/rookie-acmer/p/15088108.html class Solution { public: int diameterOfBinaryTree(TreeNode* root) { auto _ = solve(root
阅读全文
摘要:1:哈希,时间 O(n),空间 O(n) 2:最终结果是要求:nums[i] == i + 1,所以每当 nums[i] != i + 1 && nums[nums[i] - 1] != nums[i] 时,就把 nums[i] 放到他应该出现的位置,即 swap(nums[i], nums[num
阅读全文
摘要:双指针 i, j 初始值都为 0。用 j 进行遍历,当 nums[j] 不为 0 时,交换 nums[i] 与 nums[j] 的值,并 ++ i 即可。 或者当 nums[j] 不为 0 时,将 nums[j] 赋值给 nums[i],然后 ++ i。在循环结束之后,将 i 到 nums.size
阅读全文
摘要:1:利用快慢指针先找到链表的中间节点(奇数为中间,偶数为中间两个中的第一个),然后将后半部分链表进行翻转即可进行回文的比较。(或者翻转前半部分链表也是一样的). 这里是翻转前半部分链表。需要的注意:在翻转的过程中引用 head -> next 时,该值有可能被改变,即 line 28、29! 2:利
阅读全文
摘要:递归:两个对称的节点同时走即可。 迭代:用队列,每一次 push 两个节点,这两个节点是对称的! // 递归 class Solution { public: bool isSymmetric(TreeNode* root) { return _isSymmetric(root, root); }
阅读全文
摘要:没什么坑,在回溯的时候交换两儿子即可 class Solution { public: TreeNode* invertTree(TreeNode* root) { if(root == nullptr) return nullptr; invertTree(root -> left); inver
阅读全文
摘要:递归很简单,不说了。 非递归:用栈来模拟整个递归的过程,但是栈放一个 pair<TreeNode*, bool>,bool 为 false 表示当前这个节点并未向左延伸 class Solution { public: vector<int> inorderTraversal(TreeNode* r
阅读全文
摘要:暴力枚举:时间 O(n^2),空间 O(1) 哈希:时间 O(n),空间 O(n) class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, int> map; fo
阅读全文
摘要:用优先队列维护 ListNode*,根据其 val 值做小顶堆,每一次都取小顶堆堆订连接上即可 class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<ListNode*, v
阅读全文
摘要:首先,树形 dp,dp[i][1] 表示以 i 节点为起点往下到达某个节点的最大路径和(与题目的意思不一样,只能往下),dp[i][0] 表示不以 i 为起点的最大路径和。 假设当前节点为 i,左儿子为 lef,右儿子为 rig。那么 dp[i][1] = max(max(dp[lef][1], d
阅读全文
摘要:利用归并排序的思想,每一次将链表从中间分成两个链表,然后递归处理即可。 坑点:找中间节点时,如果节点是奇数个则是从中间分,如果是偶数个,则需要中间两个中前面那个分。 否则容易出现死循环的情况。最好是用 两个节点和三个节点的时候模拟一下是否正确。 class Solution { public: Li
阅读全文
摘要:直接看代码,比文字解释清楚 class Solution { public: vector<int> productExceptSelf(vector<int>& nums) { vector<int> res(nums.size(), 1); int lef = 1, rig = 1; for(i
阅读全文
摘要:两种: ① 将二叉搜索树还原,即变成有序数组。(空间是 O(n)) ② 对于二叉搜索树的一个节点,假设其左儿子子树节点数为 x,那么该节点为相比这颗子树的第 x + 1 小。 (空间是 O(log)) 代码是第 ② 中的,注意 base 变量的含义即可。 class Solution { publi
阅读全文
摘要:利用快排的原理:快排的每一次排序是将一个数放在正确的位置 index,当 index == k - 1 时,即第 k 大。 如果 index < k,递归处理右半部分,index > k,递归处理左半部分即可。 class Solution { public: int findKthLargest(
阅读全文
摘要:双向链表维护 pair<key, value>,每次用到的一个 key 都把它放到链表头部,容量满时删除链表尾部节点即可。 unordered_map<key, list<pair<key, value>>::iterator> 维护 key 与 链表节点的迭代器映射 class LRUCache
阅读全文

浙公网安备 33010602011771号