- 5. 最长回文子串">5. 最长回文子串
- 647. 回文子串">647. 回文子串
- 516. 最长回文子序列">516. 最长回文子序列
- 11. 盛最多水的容器">11. 盛最多水的容器
- 15. 三数之和">15. 三数之和
- 22. 括号生成">22. 括号生成
- 23. 合并K个升序链表">23. 合并K个升序链表
- 25. K 个一组翻转链表">25. K 个一组翻转链表
- 31. 下一个排列">31. 下一个排列
- 32. 最长有效括号">32. 最长有效括号
- 42. 接雨水">42. 接雨水
- 48. 旋转图像">48. 旋转图像
- 498. 对角线遍历">498. 对角线遍历
- 49. 字母异位词分组">49. 字母异位词分组
- 72. 编辑距离">72. 编辑距离
- 78. 子集">78. 子集
- 79. 单词搜索">79. 单词搜索
">- 90. 子集 II">90. 子集 II
- 95. 不同的二叉搜索树 II">95. 不同的二叉搜索树 II
- 96. 不同的二叉搜索树">96. 不同的二叉搜索树
- 98. 验证二叉搜索树">98. 验证二叉搜索树
- 101. 对称二叉树">101. 对称二叉树
- 146. LRU 缓存">146. LRU 缓存
- 236. 二叉树的最近公共祖先">236. 二叉树的最近公共祖先
- 241. 为运算表达式设计优先级">241. 为运算表达式设计优先级
- 621. 任务调度器">621. 任务调度器
- 494. 目标和">494. 目标和
- 416. 分割等和子集">416. 分割等和子集
- 394. 字符串解码">394. 字符串解码
- 399. 除法求值">399. 除法求值
- 347. 前 K 个高频元素">347. 前 K 个高频元素
- 221. 最大正方形">221. 最大正方形
- 337. 打家劫舍 III">337. 打家劫舍 III
- 207. 课程表">207. 课程表
- 297. 二叉树的序列化与反序列化">297. 二叉树的序列化与反序列化
- 6111. 螺旋矩阵 IV">6111. 螺旋矩阵 IV
5. 最长回文子串
只考虑以一个元素为中心时
class Solution10 {public:string longestPalindrome(string s) {int res = 0, cur = 0;for (int i=0; i<s.size(); i++){int curLen = palindromeLen(s, i);if (curLen > res)res = curLen, cur = i;}return s.substr(cur-res+1, res*2);}public:// 中心扩散时,如何处理偶数对称??int palindromeLen(string& s, int idx){int edge = min(idx, (int)s.size()-idx);int i=0;for (; i<=edge; i++)if (s[idx-i] != s[idx+i]) break;return i;}};
中心扩散法
这里的start = i - (mlen - 1) / 2这一步计算还是很精妙的,/2时向下取整了,同时包含了奇数和偶数的情况
class Solution {public:string longestPalindrome(string s) {int mlen = 0, start = 0;for (int i=0; i<s.size(); i++){// 取单中心和双中心的最大值int curLen = max(palindromeLen(s, i, i), palindromeLen(s, i, i+1));if (curLen > mlen)mlen = curLen, start = i - (mlen - 1) / 2;}return s.substr(start, mlen);}public:int palindromeLen(string& s, int l, int r){while (l >= 0 && r < s.size() && s[l] == s[r])l--, r++;return r - l - 1;}};
动态规划法
Manacher 算法
思路和代码待研究
647. 回文子串
class Solution {public:int countSubstrings(string s) {// dp[i][j] s[i,j]是否是回文子串int n = s.size(), res = 0;vector<vector<bool>> dp(n, vector<bool>(n, false));for (int i=0; i<n; i++){for (int j=0; j<=i; j++){// 只有一个字符 || 去除两边的子串是回文if (s[i] == s[j] && (i-j<2 || dp[j+1][i-1])){dp[j][i] = true;res++;}}}return res;}};
516. 最长回文子序列
这里的遍历顺序,完全是根据状态转移方程来设定的,保证用到时,已经计算出来了即可。
class Solution {public:int longestPalindromeSubseq(string s) {int n = s.size();vector<vector<int>> f(n, vector<int>(n, 0));for (int i=n-1; i>=0; i--){f[i][i] = 1;for (int j=i+1; j<n; j++){if (s[i] == s[j])f[i][j] = f[i+1][j-1] + 2;elsef[i][j] = max(f[i+1][j], f[i][j-1]);}}return f[0][n-1];}};
11. 盛最多水的容器
我的第一理解这题肯定和接雨水是一样的
其实看了答案之后,还真的没有什么差别;
class Solution {public:int maxArea(vector<int>& height) {int l = 0, r = height.size()-1;int res = 0;while (l < r){res = height[l] > height[r] ? max(res, (r-l)*height[r--]) : max(res, (r-l)*height[l++]);}return res;}};
15. 三数之和
class Solution {public:vector<vector<int>> threeSum(vector<int>& nums) {sort(nums.begin(), nums.end());vector<vector<int>> res;int n = nums.size();if (n<3) return res;for (int i=0; i<n; i++){if (nums[i]>0) break;if (i>0 && nums[i]==nums[i-1]) continue;int l = i+1, r = n-1;while (l < r){int sum = nums[i] + nums[l] + nums[r];if (sum == 0){res.push_back({nums[i], nums[l], nums[r]});while (l<r && nums[l]==nums[l+1]) l++;while (l<r && nums[r]==nums[r-1]) r--;l++, r--;}else if(sum < 0) l++;else r--;}}return res;}};
class Solution {public:vector<vector<int>> threeSum(vector<int>& arr) {vector<vector<int>> res;sort(arr.begin(), arr.end());for (int i=0; i<arr.size(); i++ ) {if (i && arr[i] == arr[i-1]) continue;for (int j = i + 1, k = arr.size()-1; j<k; j++) {if (j > i+1 && arr[j] == arr[j-1]) continue ;while (j < k-1 && arr[i] + arr[j] + arr[k-1] >= 0) k--;if (arr[i] + arr[j] + arr[k] == 0) res.push_back({arr[i], arr[j], arr[k]});}}return res;}};
22. 括号生成
合法括号序列的条件:
class Solution {public:vector<string> res;vector<string> generateParenthesis(int n) {dfs(n, 0, 0, "");return res;}/*lc : left bracket countrc : right bracket*/void dfs(int n, int lc, int rc, string seq) {if (lc == n && rc == n) res.push_back(seq);else {if (lc < n) dfs(n, lc+1, rc, seq + '(');if (rc < n && rc < lc) dfs(n, lc, rc + 1, seq + ')');}}};
23. 合并K个升序链表
class Solution {public:struct cmp {bool operator() (ListNode* a, ListNode* b) {return a->val > b->val;}};ListNode* mergeKLists(vector<ListNode*>& lists) {if (lists.empty()) return nullptr;priority_queue<ListNode*, vector<ListNode*>, cmp> heap; // smallest heapfor (auto &l : lists) if (l) heap.push(l);auto dummy = new ListNode(-1), node = dummy;while (!heap.empty()) {auto t = heap.top();heap.pop();node = node->next = t;if (t->next) heap.push(t->next);}return dummy->next;}};
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/class Solution {public:ListNode* mergeKLists(vector<ListNode*>& lists) {return merge(lists, 0, lists.size()-1);}ListNode* merge(vector<ListNode*>& lists, int l, int r) {if (l == r) return lists[l];if (l > r) return nullptr;int mid = l + r >> 1;return mergeTwo(merge(lists, l, mid), merge(lists, mid + 1, r));}ListNode* mergeTwo(ListNode* l1, ListNode* l2) {auto dummy = new ListNode(-1), node = dummy;while (l1 && l2) {if (l1->val > l2->val) node->next = l2, l2 = l2->next;\else node->next = l1, l1 = l1->next;node = node->next;}if (l1) node->next = l1;if (l2) node->next = l2;return dummy->next;}};
25. K 个一组翻转链表
翻转前先判断是否剩余节点数是否大于k
翻转的过程可以分为四步:
对应图中的1,2,3,4
ListNode* reverseKGroup(ListNode* head, int k) {auto dummy = new ListNode(-1), prev = dummy;dummy->next = head;while (prev) {auto node = prev;for (int i=0; i<k && node; i++) node = node->next;if (!node) break;auto pre = prev->next, cur = pre->next;for (int i=0; i<k-1; i++) {auto next = cur->next;cur->next = pre;pre = cur;cur = next;}auto c = prev->next;prev->next = pre;c->next = cur; // nextprev = c;}return dummy->next;}
31. 下一个排列

- 从后向前找到第一个降序的数, a[i],
- 从后往前找到第一个比a[i]大的数 a[j]
- swap(i, j)
- reverse(i+1, n)
class Solution {public:void nextPermutation(vector<int>& nums) {// 1.从后往前找到第一个比后面小的书a[i]// ==>a[i]后的数都是降序的int i = nums.size()-2;while (i >= 0 && nums[i] >= nums[i+1]) i--;if (i>=0){// 2. 从后往前找到第一个比a[i]大的数 a[j]int j = nums.size()-1;while (j >=0 && nums[j] <= nums[i]) j--;// 3. 交换swap(nums[i], nums[j]);}// 4. 交换a[i]后的数字 ==> 变为升序reverse(nums.begin()+i+1, nums.end());}};
32. 最长有效括号
有问题啊
class Solution {public:int longestValidParentheses(string s) {stack<char> st;int res = 0, stackTearIdx = 0;for (int i=0; i<s.size(); i++){char ch = s[i];if (!st.empty() && isSuit(st.top(), ch)){st.pop();res = max(res, i-(int)st.size()+1);}else{if (st.empty())stackTearIdx = i;st.push(ch);}}// "()(()" 2 how to solve this case// 当栈不为空,且栈顶元素对应的的idx+最长有效长度>字符长度时,// 出现部分无效的情况,需要取前面和后面部分的最大值if (!st.empty() && res + stackTearIdx > s.size())res = max(stackTearIdx, res-stackTearIdx);return res;}bool isSuit(char a, char b){return a == '(' && b == ')';}};
测试用例:"(()"")()())""""()(()""()(())""(())(()""((())(()"测试结果:2402646期望结果:2402644
最长回文子串思路(但是也不对啊)
class Solution {public:int longestValidParentheses(string s) {int res = 0;for (int i=0; i<s.size(); i++){int cur = max(longPa(s, i, i), longPa(s, i, i+1));res = max(res, cur);}return res;}int longPa(const string &s, int l, int r){while (l>=0 && r<s.size() && isSuit(s[l], s[r]))l--, r++;return r-l-1;}bool isSuit(char a, char b){return a == '(' && b == ')';}};
上面抓着左右括号匹配的思路是不正确的
新思路:
- 左括号:
直接压入栈对应的idx
右括号
```cpp class Solution { public: int longestValidParentheses(string s) { stackst; st.push(-1); int res = 0; for (int i=0; i<s.length(); i++) { if (s[i] == '(') st.push(i);else{st.pop();if (st.empty()) st.push(i);else res = max(res, i - st.top());}
} return res; }
};
[ref](https://leetcode.cn/problems/longest-valid-parentheses/solution/shou-hua-tu-jie-zhan-de-xiang-xi-si-lu-by-hyj8/)<br />下面的图是使用反证法证明<a name="edi81"></a>#### [33. 搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/)> 感受上这题并不难,但是我刷了好久啊> 需要加油啊T_T><br />本题可以分为三步:1. 找到两段区间的分界点1. 判断位于哪段区间1. 找出target```cppclass Solution {public:int search(vector<int>& nums, int target) {if (nums.empty()) return -1;// 1. 找到两段区间的分界点int l = 0, r = nums.size() - 1;while (l < r ) {int mid = l + r + 1>> 1;if (nums[mid] >= nums[0]) l = mid;else r = mid - 1;}// 2. 判断target位于哪个区间if (target >= nums[0]){ // target位于第一段区间l = 0;} else { //target位于第er段区间l = r + 1, r = nums.size() - 1;}while (l < r) {int mid = l + r + 1 >> 1;if (nums[mid] <= target) l = mid;else r = mid - 1;}if (nums[r] == target) return r; // 通常使用的是l,这里使用的是relse return -1;}};
int search(vector<int>& nums, int target) {int l = 0, r = nums.size() - 1;while (l <= r){int mid = l + r >> 1;if (nums[mid] == target) return mid;// left is sortedif (nums[l] <= nums[mid]){if (target >= nums[l] && target <= nums[mid]) // in [l, mid)r = mid - 1;elsel = mid + 1;}else // right is sorted{if (target >= nums[mid] && target <= nums[r]) // in (mid, r]l = mid + 1;elser = mid - 1;}}return -1;}
42. 接雨水
- 双指针 解法

这里需要思考一个问题,为啥低的要先移动?
若是高的先移动,马上就会得到,根据下面的算法,就会得到第二个也能接雨水,若是后面没有出现比第二个高的柱子,这里会出现矛盾
class Solution {public:int trap(vector<int>& h) {int res = 0;int i = 0, j = h.size() - 1;int lm = h[i], rm = h[j];while ( i < j) {if (h[i] <= h[j]) {if (h[i] <= lm) res += (lm - h[i]);else lm = h[i];i++;} else {if (h[j] < rm) res += (rm - h[j]);else rm = h[j];j--;}}return res;}};
- 栈
感觉这个解法有点难懂233

class Solution {public:int trap(vector<int>& height) {stack<int> stk;int res = 0;for (int i = 0; i < height.size(); i ++) {//记录栈中的上一个柱子的高度int last = 0;while (stk.size() && height[stk.top()] <= height[i]) {res += (height[stk.top()] - last) * (i - stk.top() - 1);last = height[stk.top()];stk.pop();}if (stk.size()) res += (i - stk.top() - 1) * (height[i] - last);stk.push(i);}return res;}};
48. 旋转图像

先水平翻转,然后对角线翻转
class Solution {public:// 先上下翻转,然后对角翻转void rotate(vector<vector<int>>& matrix) {int n = matrix[0].size();// 水平翻转for (int i=0; i<n/2; i++) swap(matrix[i], matrix[n-i-1]);// 对角翻转for (int i=0 ; i<n; i++)for (int j=0; j<i; j++)swap(matrix[i][j], matrix[j][i]);}};
498. 对角线遍历

class Solution {public:vector<int> findDiagonalOrder(vector<vector<int>>& mat) {vector<int> res;int n = mat.size(), m = mat[0].size();for (int i=0; i<m+n-1; i++) // 一共有m+n-1条对角线{if(i & 1) //奇数行对角线{for (int x=max(0, i-m+1); x<=min(i, n-1); x++)res.push_back(mat[x][i-x]);}else{for (int x=min(i, n-1); x>=max(0, i-m+1); x--)res.push_back(mat[x][i-x]);}}return res;}};
49. 字母异位词分组
这里有一个很简单的思路,就是对string进行排序,将所有排序相同的元素放到一个value中
class Solution {public:vector<vector<string>> groupAnagrams(vector<string>& strs) {unordered_map<string, vector<string>> mp;for (auto& str : strs){string key(26, 0);for (auto& ch : str) key[ch-'a']++;/*string key = str;sort(key.begin(), key.end());*/mp[key].push_back(str);}vector<vector<string>> res;for (auto iter : mp)res.push_back(iter.second);return res;}};
72. 编辑距离
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符 删除一个字符 替换一个字符
class Solution {public:int minDistance(string s1, string s2) {int n1= s1.size(), n2 = s2.size();vector<vector<int>> f(n1, vector<int>(n2, INT_MAX));for (int i=0; i<n1; i++) {for (int j=0; j<n2; j++) {if (!i && !j) f[i][j] = 0;else if (!i) f[i][j] = j;else if (!j) f[i][j] = i;else {f[i][j] =min(f[i][j], min(f[i][j-1], f[i-1][j]) + 1);if (s1[i] == s2[j]) f[i][j] = min(f[i][j], f[i-1][j-1]);else f[i][j] = min(f[i][j], f[i-1][j-1] + 1);}}}return f[n1-1][n2-1];}};
如果随手写,大概率会写出上面的代码,那么存在一个问题,就是f[0][0]的场景默认是给了一个相等0, f[i][0]也是默认的是f[0][0]相等
单独处理s1[0] s2[0]的场景目前还是存在一些问题
最后还是将:f变大计算时,更好处理
class Solution {public:int minDistance(string s1, string s2) {int n1= s1.size(), n2 = s2.size();vector<vector<int>> f(n1+1, vector<int>(n2+1, INT_MAX));for (int i=0; i<=n1; i++) {for (int j=0; j<=n2; j++) {if (!i && !j) f[i][j] = 0;else if (!i) f[i][j] = j;else if (!j) f[i][j] = i;else {f[i][j] =min(f[i][j], min(f[i][j-1], f[i-1][j]) + 1);if (s1[i-1] == s2[j-1]) f[i][j] = min(f[i][j], f[i-1][j-1]);else f[i][j] = min(f[i][j], f[i-1][j-1] + 1);}}}return f[n1][n2];}};
78. 子集
由于n位的二进制数对应的01可以表示所有的选和不选的方案,所以可以采用如下的迭代的方法
时间复杂度O()
class Solution {public:vector<vector<int>> subsets(vector<int>& nums) {vector<vector<int>> res;int n = nums.size();for (int i=0; i< 1<<n; i++) {vector<int> path;for (int j=0; j<n; j++) {if (i>>j & 1) path.push_back(nums[j]);}res.push_back(path);}return res;}};
递归的方式
时间复杂度O()
class Solution {public:vector<vector<int>> res;vector<int> cur;vector<int> nums;vector<vector<int>> subsets(vector<int>& nums) {this->nums = nums;dfs(0);return res;}void dfs(int idx) {res.push_back(cur);for (int i = idx; i<nums.size(); i++) {cur.push_back(nums[i]);dfs(i+1);cur.pop_back();}}};
79. 单词搜索
这里是使用特殊符号标记,然后回复原来的字符。以此来减少数组的开销
class Solution {public:bool exist(vector<vector<char>>& g, string w) {for (int i=0; i<g.size(); i++)for (int j=0; j<g[i].size(); j++)if (dfs(g, w, i, j, 0)) return true;return false;}int dx[4] = {-1, 0, 1, 0};int dy[4] = {0, -1, 0, 1};bool dfs(vector<vector<char>> &g, string &w, int x, int y, int idx) {if (g[x][y] != w[idx]) return false;if (idx == w.size() - 1) return true;char t = g[x][y];g[x][y] = '.';for (int i = 0; i<4; i++) {int a = x + dx[i], b = y + dy[i];if (a<0 || a>=g.size() || b<0 || b>=g[0].size() || g[a][b] == '.') continue;if (dfs(g, w, a, b, idx + 1)) return true;}g[x][y] = t;return false;}};
90. 子集 II
class Solution {private:vector<vector<int>> res_;vector<int> nums_;vector<int> combi_;vector<bool> used_;int n_;public:vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(), nums.end());n_ = nums.size();nums_ = nums;used_ = vector<bool>(n_, false);dfs(0, 0);return res_;}void dfs(int idx, int count){if (count == n_){res_.push_back(combi_);return;}for (int i=idx; i<n_; i++){dfs(i+1, count+1); // this is differentif (i>0 && nums_[i]==nums_[i-1] && !used_[i-1])continue;combi_.push_back(nums_[i]);used_[i] = true;dfs(i+1, count+1);used_[i] = false;combi_.pop_back();}}};
这里有个不一样到地方,就是将不选在循环的每一步都执行

通过绘图可以发现,每次dfs时,插入一个元素,得到都是一个子集,只需要注意剪枝即可
class Solution {private:vector<vector<int>> res_;vector<int> nums_;vector<int> combi_;vector<bool> used_;int n_;public:vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(), nums.end());n_ = nums.size();nums_ = nums;used_ = vector<bool>(n_, false);dfs(0);return res_;}void dfs(int idx){res_.push_back(combi_);for (int i=idx; i<n_; i++){if (i>0 && nums_[i]==nums_[i-1] && !used_[i-1])continue;combi_.push_back(nums_[i]);used_[i] = true;dfs(i+1);used_[i] = false;combi_.pop_back();}}};
加一个y总常用的去重解法;
- 统计每一个元素的个数,
- 然后依次枚举,
- 最后依次弹出
class Solution {public:vector<vector<int>> res;vector<int> path;vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(), nums.end());dfs(nums, 0);return res;}void dfs(vector<int> &nums, int u) {if (u == nums.size()) {res.push_back(path);return ;}int k = u + 1;while (k<nums.size() && nums[k] == nums[k-1]) k++;for (int i=0; i <= k-u; i++) {dfs(nums, k);path.push_back(nums[u]); // 先递归,后加入元素}for (int i=0; i<=k-u; i++)path.pop_back();}};
output:
[[],[2],[2,2],[1],[1,2],[1,2,2]]
95. 不同的二叉搜索树 II

class Solution {
public:
vector<TreeNode*> generateTrees(int n) {
if (n == 0) return {nullptr};
return dfs(1, n);
}
vector<TreeNode*> dfs(int l, int r) {
if (l > r) return {nullptr};
vector<TreeNode*> res;
for (int k=l; k<=r; k++) {
auto left = dfs(l, k-1), right = dfs(k+1, r);
for (auto i : left) {
for (auto j : right) {
auto root = new TreeNode(k);
root->left = i, root->right = j;
res.push_back(root);
}
}
}
return res;
}
};
类似题型可见 241
96. 不同的二叉搜索树

class Solution {
public:
int numTrees(int n) {
vector<int> f(n+1);
f[0] = 1;
for (int i=1; i<=n; i++)
{
for (int j=1; j<=i; j++)
f[i] += f[j - 1] * f[i - j];
}
return f[n];
}
};
98. 验证二叉搜索树
class Solution {
public:
long pre = LONG_MIN;
public:
bool isValidBST(TreeNode* root) {
if (root == nullptr) return true;
bool left = isValidBST(root->left);
if (root->val <= pre) return false;
pre = root->val;
bool right = isValidBST(root->right);
return left && right;
}
};
根据二叉搜索树的中序遍历为有序来进行
101. 对称二叉树
写判断条件的时候不要重叠某些情况
class Solution {
public:
bool isSymmetric(TreeNode* root) {
return dfs(root->left, root->right);
}
bool dfs(TreeNode* l, TreeNode* r)
{
if (!l && !r) return true;
else if (!l || !r || l->val != r->val) return false;
return dfs(l->left, r->right) && dfs(l->right, r->left);
}
};
146. LRU 缓存
class LRUCache {
public:
int cap_;
list<int> l_;
unordered_map<int, int> mp;
public:
LRUCache(int capacity) : cap_(capacity) {
}
/*
查找要完成更新的过程
1. 删除key
2. 将key移动到最新的地方
*/
int get(int key) {
auto iter = mp.find(key);
if (iter != mp.end())
{
updateList
return iter->second;
}
return -1;
}
/*
首先用哈希表判断key是否存在:
如果key存在:
则修改对应的value,同时将key对应的节点放到双链表的最左侧;
如果key不存在:
1. 如果缓存已满,则删除双链表最右侧的节点(上次使用时间最老的节点),更新哈希表;
2. 否则,插入(key, value):同时将key对应的节点放到双链表的最左侧,更新哈希表;
*/
void put(int key, int value) {
auto iter = mp.find(key);
if (iter != mp.end()) // key exist
{
updateList(key);
iter->second = value;
}
else // key don't exist
{
if (mp.size() == cap_) //
{
auto front = l_.front();
l_.pop_front();
mp.erase(front);
}
mp.insert({key, value});
l_.push_back(key);
}
}
// 在链表中找到当前元素,并且移动到链表的最右端
// 我这里是直接删除,然后插入
void updateList(int key)
{
for (auto iter=l_.begin(); iter!=l_.end(); iter++)
{
bool flag = false;
if (*iter == key)
{
l_.erase(iter);
flag = true;
break;
}
if (flag) break;
}
l_.push_back(key);
}
};
这个解法非常的费时。。。
236. 二叉树的最近公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr || root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left == nullptr) return right;
if (right == nullptr) return left;
return root;
}
};
241. 为运算表达式设计优先级
class Solution {
public:
vector<string> e;
vector<int> diffWaysToCompute(string exp) {
transfer(exp);
return dfs(0, e.size() - 1);
}
vector<int> dfs(int l, int r) {
if (l == r) return {stoi(e[l])};
vector<int> res;
for (int i=l+1; i < r; i += 2) {
auto left = dfs(l, i-1), right = dfs(i+1, r);
for (auto x : left) {
for (auto y : right) {
int z;
if (e[i] == "+") z = x + y;
else if (e[i] == "-") z = x - y;
else z = x * y;
res.push_back(z);
}
}
}
return res;
}
void transfer(const string& s) {
for (int i=0; i<s.size(); i++) {
if (isdigit(s[i])) {
int j = i, x = 0;
while (j < s.size() && isdigit(s[j]))
x = x * 10 + (s[j++] - '0');
i = j - 1;
e.push_back(to_string(x));
} else {
e.push_back(s.substr(i, 1));
}
}
}
};
621. 任务调度器
class Solution {
public:
int leastInterval(vector<char>& tasks, int n) {
int maxTimes = 0; //出现次数最多的任务的次数
int maxCnt = 0; //
unordered_map<char, int> mp;
for (const auto& ch : tasks)
{
mp[ch]++;
maxTimes = max(maxTimes, mp[ch]);
}
for (auto iter : mp)
if (iter.second == maxTimes)
maxCnt++;
return max((int)tasks.size(), (maxTimes-1)*(n+1) + maxCnt);
}
};
494. 目标和
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-‘ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-‘ ,然后串联起来得到表达式 “+2-1” 。 返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目
dfs
class Solution {
vector<int> nums;
int tar;
int n;
int res;
public:
int findTargetSumWays(vector<int>& nums, int target) {
n = nums.size();
this->nums = nums;
tar = target;
dfs(0, 0);
return res;
}
void dfs(int idx, int curSum)
{
if (idx == n)
{
if (curSum == tar) res++;
return ;
}
dfs(idx+1, curSum + nums[idx]);
dfs(idx+1, curSum - nums[idx]);
return ;
}
};
416. 分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
class Solution {
public:
bool canPartition(vector<int>& nums) {
/*
1. 先求数组的总和sum
a. sum奇数, 返回false
b. sum偶数
i. 将问题转化为01背包问题,然后进行求解
target = sum/2
*/
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum & 1) return false;
int n = nums.size(), m = sum / 2;
vector<vector<int>> f(n+1, vector<int>(m+1, 0));
for (int i=1; i<=n; i++)
{
for (int j=1; j<=m; j++)
{
int wv = nums[i-1];
if (j >= wv)
f[i][j] = max(f[i-1][j], f[i-1][j-wv] + wv);
else
f[i][j] = f[i-1][j];
}
}
if (f[n][m] == m) return true;
return false;
}
};
空间优化
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum & 1) return false;
int n = nums.size(), m = sum / 2;
vector<int> f(m+1, 0);
for (int i=1; i<=n; i++)
{
int wv = nums[i-1];
for (int j=m; j>=wv; j--)
f[j] = max (f[j], f[j-wv] + wv);
}
if (f[m] == m) return true;
return false;
}
};
394. 字符串解码
这里使用的是模拟
class Solution {
public:
string decodeString(string s) {
stack<string> st;
for (auto ch : s)
{
if (ch == ']')
{
string cur, curCombi;
while (st.top()[0] != '[')
{
string c = st.top();
st.pop();
cur = c + cur;
}
st.pop(); // [ pop
string num;
while (!st.empty() && isdigit(st.top()[0])) // 注意判空
{
num = st.top() + num;
st.pop();
}
int n = stoi(num);
while (n--) curCombi += cur;
st.push(curCombi);
}
else
st.push(string(1, ch));
}
string res;
while (!st.empty())
{
res = st.top() + res;
st.pop();
}
return res;
}
};
399. 除法求值
class Solution {
public:
vector<double> calcEquation(vector<vector<string>>& eq, vector<double>& vals, vector<vector<string>>& qry){
// 建图
unordered_map<string, int> hash;
int n = eq.size(), cnt = 0; // 图中有多少条边
for (int i=0; i<n; i++) {
if (hash.find(eq[i][0]) == hash.end()) hash[eq[i][0]] = cnt++;
if (hash.find(eq[i][1]) == hash.end()) hash[eq[i][1]] = cnt++;
}
vector<vector<double>> g(cnt, vector<double>(cnt, -1.0)); // g(graph)
for (int i=0; i<n; i++) {
int k1 =hash[eq[i][0]];
int k2 = hash[eq[i][1]];
g[k1][k2] = vals[i];
g[k2][k1] = 1.0 / vals[i];
}
// floyd update
for (int k=0; k<cnt; k++) {
for (int i=0; i<cnt; i++) {
for (int j=0; j<cnt; j++) {
if (g[i][k]>0 && g[k][j]>0) // 已经构建了边(初始化时,都为-1)
g[i][j] = g[i][k] * g[k][j];
}
}
}
// get query answer
vector<double> res;
for (auto& q : qry) {
double ans = -1.0;
if (hash.find(q[0]) != hash.end() && hash.find(q[1]) != hash.end()) {
int k1 = hash[q[0]], k2 = hash[q[1]];
if (g[k1][k2] > 0)
ans = g[k1][k2];
}
res.push_back(ans);
}
return res;
}
};
347. 前 K 个高频元素
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> mp;
for (auto& i : nums) mp[i]++;
struct mycmp {
bool operator() (pair<int, int>&p1, pair<int, int>& p2) {
return p1.second > p2.second;
}
};
priority_queue<pair<int, int>, vector<pair<int, int>>, mycmp> q;
for (auto &i : mp) {
q.push(i);
if (q.size() > k) q.pop();
}
vector<int> res;
while (!q.empty()) {
res.emplace_back(q.top().first);
q.pop();
}
return res;
}
};
221. 最大正方形

class Solution {
public:
int maximalSquare(vector<vector<char>>& mat) {
int m = mat.size(), n = mat[0].size();
int res = 0;
vector<vector<int>> f(m+1, vector<int>(n+1, 0));
for (int i=0; i<m; i++) f[i][0] = mat[i][0] - '0', res = max(res, f[i][0]);
for (int i=0; i<n; i++) f[0][i] = mat[0][i] - '0', res = max(res, f[0][i]);
for (int i=1; i<m; i++) {
for (int j=1; j<n; j++) {
if (mat[i][j] == '1') {
f[i][j] = min(f[i-1][j-1], min(f[i-1][j], f[i][j-1])) + 1;
res = max(res, f[i][j]);
}
}
}
return res*res;
}
};
将第一行和第一列的处理放到循环中
class Solution {
public:
int maximalSquare(vector<vector<char>>& mat) {
int m = mat.size(), n = mat[0].size();
vector<vector<int>> f(m+1, vector<int>(n+1, 0));
int res = 0;
for (int i=0; i<m; i++) {
for (int j=0; j<n; j++) {
if (mat[i][j] == '1') {
if (i == 0 || j == 0) f[i][j] = 1;
else f[i][j] = min(f[i-1][j-1], min(f[i-1][j], f[i][j-1])) + 1;
}
res = max(res, f[i][j]);
}
}
return res*res;
}
};
337. 打家劫舍 III
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额
状态机模型
class Solution {
public:
unordered_map<TreeNode*, pair<int, int>> f;// first : not choose second : choose
int rob(TreeNode* root) {
dfs(root);
return max(f[root].first, f[root].second);
}
void dfs(TreeNode* node)
{
if (node == nullptr) return ;
dfs(node->left);
dfs(node->right);
f[node].second = node->val + f[node->left].first + f[node->right].first;
f[node].first = max(f[node->left].first, f[node->left].second) \
+ max(f[node->right].first, f[node->right].second);
}
};
207. 课程表
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false
有向无环图的拓扑排序
class Solution {
public:
bool canFinish(int n, vector<vector<int>>& edges) {
/*
有向无环图的拓扑排序
*/
vector<vector<int>> g(n);
vector<int> d(n); // in-degree
for (auto &e : edges) {
int a = e[0], b = e[1];
g[b].push_back(a); // g[b] b's next point
d[a]++; // a's in-degree ++
}
// find first in-degree == 0, push into queue
queue<int> q;
for (int i=0; i<n; i++)
if (d[i] == 0)
q.push(i);
int cnt = 0;
while (!q.empty()) {
auto t = q.front();
q.pop();
cnt++;
for (auto& i : g[t]) { // tranverse all g[t]'s next point in graph
if (--d[i] == 0)
q.push(i);
}
}
return cnt == n;
}
};
297. 二叉树的序列化与反序列化
采用的是先序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
string path;
public:
/*
input : [1,2,3,null,null,4,5]
seralize : "[1,2,3,#,#,4,5]"
*/
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
dfs_serialize(root);
return path;
}
void dfs_serialize(TreeNode* root) {
if (!root) path += "#,";
else {
path += to_string(root->val) + ',';
dfs_serialize(root->left);
dfs_serialize(root->right);
}
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int u = 0;
return dfs_deserialize(data, u);
}
TreeNode* dfs_deserialize(string& data, int& u) {
if (data[u] == '#') {
u += 2;
return nullptr;
} else {
int k = u;
while (data[u] != ',') u++;
auto root = new TreeNode(stoi(data.substr(k, u-k)));
u++; // skip ","
root->left = dfs_deserialize(data, u);
root->right = dfs_deserialize(data, u);
return root;
}
}
};
// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));
6111. 螺旋矩阵 IV
朴素做法
class Solution {
public:
vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {
vector<vector<int>> res(m, vector<int>(n, -1));
int l = 0, r = n-1, u = 0, d = m-1;
while (head) {
// l - > r
for (int i=l; i<=r && head; i++) {
res[u][i] = head->val;
head = head->next;
}
u++;
// u -> d
for (int i = u; i <= d && head; i++) {
res[i][r] = head->val;
head = head->next;
}
r--;
// r -> l
for (int i = r; i >= l && head; i--) {
res[d][i] = head->val;
head = head->next;
}
d--;
// d -> u
for (int i = d; i >= u && head; i--) {
res[i][l] = head->val;
head = head->next;
}
l++;
}
return res;
}
};

class Solution {
public:
vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {
vector<vector<int>> res(m, vector<int>(n, -1));
int x = 0, y = 0 , d = 1;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; //
for (int i = 0; i <= m * n && head; i++) {
res[x][y] = head->val;
int a = x + dx[d], b = y + dy[d];
if (a < 0 || a >= m || b < 0 || b >= n || res[a][b] != -1) { // 碰壁
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
x = a, y = b;
head = head->next;
}
return res;
}
};

