[朝花夕拾] LeetCode刷题记录
前言:
20220228
1. 两数之和
本来不想做的,无聊点开题解才发现竟然不是简单的 A + B Problem。
方法一:暴力枚举
1 class Solution { 2 public: 3 vector<int> twoSum(vector<int>& nums, int target) { 4 int n = nums.size(); 5 for (int i = 0; i < n - 1; i++) 6 for (int j = i + 1; j < n; j++) 7 if (nums[i] + nums[j] == target) 8 return {i, j}; 9 return {}; 10 } 11 };
注意:L9 的 return {} 不能省略,尽管题目说明了不会存在没有结果的情况,但对于编译器而言则必须要求在所有情况下都有返回值。
方法二:哈希表
由于答案对是唯一的,我们可以只扫描一次,用 target 减去当前值 nums[i],再判断 nums 中是否有余下的数。由于数据范围远大于数据量,采用哈希表可节省大量空间。在 C++ 中一般使用 map 实现哈希表。
1 class Solution { 2 public: 3 vector<int> twoSum(vector<int>& nums, int t) { 4 int n = nums.size(); 5 map<int, int> mp; 6 for (int i = 0; i < n; i++) { 7 auto it = mp.find(t - nums[i]); 8 if (it != mp.end()) 9 return {it -> second, i}; 10 mp[nums[i]] = i; 11 } 12 return {}; 13 } 14 };
2. 两数相加
本质上考察链表的处理。
1 class Solution { 2 public: 3 ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { 4 int c = 0; 5 ListNode *h = NULL, *t = NULL; 6 while (l1 || l2) { 7 int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + c; 8 if (!h) 9 h = t = new ListNode(sum % 10); 10 else { 11 t->next = new ListNode(sum % 10); 12 t = t->next; 13 } 14 c = sum / 10; 15 if (l1) 16 l1 = l1->next; 17 if (l2) 18 l2 = l2->next; 19 } 20 if (c > 0) 21 t->next = new ListNode(c); 22 return h; 23 } 24 };
3. 无重复字符的最长子串
方法:双指针法(LC官方写的是滑动窗口)
设置左指针和右指针,右指针依次向右移动,如果指向字符之前出现在某个位置,且该位置在左指针的左侧,则说明为了维持无重复字符,则左指针需要跳到该位置的后一个位置;
所以这个过程中需要维护一个数组 a,a[i] 表示字符 i 上次出现的位置。两个指针在移动过程中,其间的区间大小一直在变化,而变化过程中的最大值即为答案。
(官方的题解的左指针是依次向右移动而非跳跃的,大概是因为使用的 set,这样相当于用更多时间换取了更少空间)
1 const int N = 130; 2 3 class Solution { 4 public: 5 int lengthOfLongestSubstring(string s) { 6 int l = s.length(), h = 0, t = 0, ans = 0, a[N]; 7 memset(a, -1, sizeof(a)); 8 while (t < l) { 9 if (a[s[t]] != -1) 10 h = max(h, a[s[t]] + 1); 11 a[s[t]] = t; 12 ans = max(ans, t - h + 1); 13 t++; 14 } 15 return ans; 16 } 17 };
20220301
4. 不会做
5. 最长回文子串
方法一:枚举(LC官方写的中心扩展算法)
枚举每个字符,以其为回文串的中心点向左向右扩展,直到不满足回文串要求。偶回文串与奇回文串需要分开讨论。
1 class Solution { 2 public: 3 string longestPalindrome(string s) { 4 int l = s.length(), ans = 1, st = 0; 5 for (int i = 0; i < l; i++) { 6 int j = 0; 7 while (j <= i && i + j < l && s[i - j] == s[i + j]) 8 j++; 9 if (2 * j - 1 > ans) 10 st = i - j + 1, ans = 2 * j - 1; 11 } 12 for (int i = 0; i < l - 1; i++) { 13 if (s[i] != s[i + 1]) continue; 14 int j = 1; 15 while (j <= i && i + j - 1 < l && s[i - j] == s[i + j + 1]) 16 j++; 17 if (2 * j > ans) 18 st = i - j + 1, ans = 2 * j; 19 } 20 return s.substr(st, ans); 21 } 22 };
P.S. string 的截取函数为 substr。
其他:时间复杂度 O(n) 的 Manacher 算法与 O(n ^ 2) 的动态规划。
7. 整数反转
1 class Solution { 2 public: 3 int reverse(int x) { 4 long long o = 0; 5 while (x) o *= 10, o += x % 10, x /= 10; 6 if (o < INT_MIN || o > INT_MAX) return 0; 7 return o; 8 } 9 };
8. 字符串转换整数(atoi)
1 class Solution { 2 public: 3 int myAtoi(string s) { 4 int o = 0, l = s.length(), f = 0; 5 long long ans = 0; 6 while (o < l && s[o] == ' ') o++; 7 if (o < l && s[o] == '+') f = 0, o++; 8 else if (o < l && s[o] == '-') f = 1, o++; 9 while (o < l && s[o] == '0') o++; 10 while (o < l && s[o] >= '0' && s[o] <= '9') { 11 ans *= 10, ans += s[o] - '0', o++; 12 if (ans > INT_MAX) return f ? INT_MIN : INT_MAX; 13 } 14 if (f) ans = -ans; 15 return ans; 16 } 17 };
20220302
剑指 Offer 3. 数组中重复的数字
方法一:排序
将数组排序,然后查找是否存在相邻且相等的元素。
1 class Solution { 2 public: 3 int findRepeatNumber(vector<int>& a) { 4 int l = a.size(); 5 sort(a.begin(), a.end()); 6 for (int i = 1; i < l; i++) 7 if (a[i] == a[i - 1]) 8 return a[i]; 9 return 0; 10 } 11 };
方法二:枚举查重
把读入的元素进行记录(使用数组、map、set均可),出现记录过的数字即表示重复元素。
1 class Solution { 2 public: 3 int findRepeatNumber(vector<int>& a) { 4 int l = a.size(); 5 set <int> s; 6 for (int i = 0; i < l; i++) { 7 if (s.count(a[i])) return a[i]; 8 s.insert(a[i]); 9 } 10 return 0; 11 } 12 };
优化:
题目有个限定,即值域与定义域均为 n,当且仅当没有重复数字时,元素与其索引编号为一一对应的,均为 0 ~ n - 1;
于是可以把读入的元素 i 放入大小为 n 的另一个数组 b 的第 i 个位置,如果有两个元素需要放到同一个位置,则说明为重复元素。
class Solution { public: int findRepeatNumber(vector<int>& a) { map <int, bool> mp; for (int i : a) { if (mp[i]) return i; mp[i] = 1; } return 0; } };
方法三:原地哈希
在上述优化的基础上,我们发现多开一个数组其实是赘余的:我们可以把上述过程理解成,将打乱的全排列重新排序,通过不停交换元素位置来实现,而唯一不同的是它实际上并非全排列,当且仅当交换过程中出现冲突,即找到了重复元素。
以样例为例,[2, 3, 1, 0, 2, 5, 3],第一个元素 2 理应放在第 2 个位置,于是将其与第 2 个位置的元素交换,得 [1, 3, 2, 0, 2, 5, 3],同理再将 1 与第 1 个位置元素交换,得 [3, 1, 2, 0, 2, 5, 3],再得 [0, 1, 2, 3, 2, 5, 3],第 0 个位置满足条件;
依次往后遍历,0, 1, 2, 3 均已就位,第 4 个位置为 2,其理应放在第 2 个位置,但是我们发现第 2 个位置已经有 2 了,说明数组里有多个 2,输出 2 即可。
1 class Solution { 2 public: 3 int findRepeatNumber(vector<int>& a) { 4 int l = a.size(); 5 for (int i = 0; i < l; i++) 6 while (i != a[i]) { 7 if (a[a[i]] == a[i]) return a[i]; 8 swap(a[i], a[a[i]]); 9 } 10 return 0; 11 } 12 };
剑指 Offer 4. 二维数组中的查找
方法一:DFS + 简单优化
剪枝剪掉了反向查找,但最坏复杂度依旧是 O(nm)
1 class Solution { 2 public: 3 bool dfs(int x, int y, vector<vector<int>>& a, int target) { 4 if (x >= a.size() || y >= a[0].size() || x < 0 || y < 0 || f[x][y]) return 0; 5 f[x][y] = 1; 6 if (a[x][y] == target) return 1; 7 if (a[x][y] < target) 8 return dfs(x + 1, y, a, target) || dfs(x, y + 1, a, target); 9 else 10 return dfs(x - 1, y, a, target) || dfs(x, y - 1, a, target); 11 } 12 bool findNumberIn2DArray(vector<vector<int>>& a, int target) { 13 return dfs(0, 0, a, target); 14 } 15 };
方法二:线性查找
看了题解之后明白了,从左上角出发真是个新奇的思路,大了往左走,小了往下走,必然会找到答案,这样就降到了 O(n + m)。
class Solution { public: bool findNumberIn2DArray(vector<vector<int>>& a, int target) { if (!a.size() || !a[0].size()) return 0; int x = 0, y = a[0].size() - 1; while (x < a.size() && y >= 0) { if (a[x][y] == target) return 1; if (a[x][y] < target) x++; else y--; } return 0; } };
注意边界条件。
其他:直接暴力找
20220303
剑指 Offer 5. 替换空格
1 class Solution { 2 public: 3 string replaceSpace(string s) { 4 int l = s.length(); 5 string ans; 6 for (auto& c : s) 7 if (s == ' ') ans += "%20"; 8 else ans += s; 9 return ans; 10 } 11 };
剑指 Offer 6. 从尾到头打印链表
方法:栈
1 class Solution { 2 public: 3 vector<int> reversePrint(ListNode* head) { 4 stack<int> s; 5 vector<int> a; 6 ListNode* p = head; 7 while (p) 8 s.push(p->val), p = p->next; 9 while (!s.empty()) 10 a.push_back(s.top()), s.pop(); 11 return a; 12 } 13 };
剑指 Offer 7. 重建二叉树
方法:递归
利用前序遍历与中序遍历之间的关系:每从前序遍历中读到一个结点,便在中序遍历中找到该结点(用 map 预处理索引),该结点在中序遍历位置的左侧为以该结点为根的子树的左子树,右侧为右子树,依次构造即可。
1 class Solution { 2 private: 3 map<int, int> ind; 4 public: 5 TreeNode* build(vector<int>& p, vector<int>& i, int pl, int pr, int il, int ir) { 6 if (pl > pr) return nullptr; 7 int root_p = pl, root_i = ind[p[pl]]; 8 TreeNode* root = new TreeNode(p[pl]); 9 int size = root_i - il; 10 root->left = build(p, i, pl + 1, pl + size, il, root_i - 1); 11 root->right = build(p, i, pl + size + 1, pr, root_i + 1, ir); 12 return root; 13 } 14 15 TreeNode* buildTree(vector<int>& p, vector<int>& i) { 16 int n = p.size(); 17 for (int o = 0; o < n; o++) 18 ind[i[o]] = o; 19 return build(p, i, 0, n - 1, 0, n - 1); 20 } 21 };
20220304
剑指 Offer 9. 用两个栈实现队列
考研的时候看到过这道题。栈 A 用来入队;出队时,将栈 A 的元素倒入栈 B,再出栈(出队)。
1 class CQueue { 2 stack<int> s1, s2; 3 public: 4 CQueue() { 5 while (!s1.empty()) 6 s1.pop(); 7 while (!s2.empty()) 8 s2.pop(); 9 } 10 11 void appendTail(int value) { 12 s1.push(value); 13 } 14 15 int deleteHead() { 16 if (s2.empty()) 17 while (!s1.empty()) 18 s2.push(s1.top()), s1.pop(); 19 if (s2.empty()) 20 return -1; 21 int res = s2.top(); 22 s.pop(); 23 return res; 24 } 25 };
剑指 Offer 10. 斐波那契数列 + 青蛙跳台阶问题
1 const int MOD = 1e9 + 7; 2 3 class Solution { 4 public: 5 int fib(int n) { 6 int a = 0, b = 1; 7 for (int i = 1; i <= n; i++) 8 ((i & 1) ? a : b) = (a + b) % MOD; 9 return n & 1 ? b : a; 10 } 11 };
20220305
剑指 Offer 11. 旋转数组的最小数字
方法一:暴力
1 class Solution { 2 public: 3 int minArray(vector<int>& numbers) { 4 int mi = INT_MAX; 5 for (int i : numbers) 6 mi = min(mi, i); 7 return mi; 8 } 9 };
方法二:二分查找
根据题意,序列基本是升序的,只有最小值出现的一个位置是降序的,而我们需要找的就是这个位置,不难想到用二分来做。
1 class Solution { 2 public: 3 int minArray(vector<int>& a) { 4 int n = a.size(); 5 int l = 0, r = n - 1; 6 while (l < r) { 7 int m = (l + r) >> 1; 8 if (a[m] < a[r]) r = m; 9 else if (a[m] > a[r]) l = m + 1; 10 else r--; 11 } 12 return a[l]; 13 } 14 };
剑指 Offer 12. 矩阵中的路径
方法:DFS + 剪枝
1 const int vx[4] = {0, 0, 1, -1}, vy[4] = {1, -1, 0, 0}; 2 3 class Solution { 4 public: 5 bool dfs(vector<vector<char>>& b, vector<vector<char>>& f, int n, int m, string word, int l, int x, int y, int d) { 6 f[x][y] = 1; 7 if (d == l - 1) 8 return 1; 9 bool res = 0; 10 for (int i = 0; i < 4; i++) { 11 int tx = x + vx[i], ty = y + vy[i]; 12 if (tx >= 0 && tx < n && ty >= 0 && ty < m && !f[tx][ty] && b[tx][ty] == word[d + 1]) 13 res = dfs(b, f, n, m, word, l, tx, ty, d + 1); 14 if (res) return 1; 15 } 16 f[x][y] = 0; 17 return 0; 18 } 19 bool exist(vector<vector<char>>& b, string word) { 20 int n = b.size(), m = b[0].size(), l = word.length(); 21 vector<vector<char>> f(n, vector<char>(m)); 22 for (int i = 0; i < n; i++) 23 for (int j = 0; j < m; j++) 24 if (b[i][j] == word[0]) { 25 for (int k = 0; k < n; k++) 26 f[k].assign(m, 0); 27 if (dfs(b, f, n, m, word, l, i, j, 0)) return 1; 28 } 29 return 0; 30 } 31 };
20220307 剑指 Offer 13. 机器人的运动范围
方法:DFS + 剪枝
1 const int vx[4] = {0, 0, 1, -1}, vy[4] = {1, -1, 0, 0}; 2 const int N = 1e2 + 5; 3 4 class Solution { 5 int ans = 0, f[N][N]; 6 public: 7 int calc(int o) { 8 return o < 10 ? o : o == 100 ? 1 : o % 10 + o / 10; 9 } 10 void dfs(int x, int y, int m, int n, int k) { 11 f[x][y] = 1, ans++; 12 for (int i = 0; i < 4; i++) { 13 int tx = x + vx[i], ty = y + vy[i]; 14 if (tx >= 0 && ty >= 0 && tx < m && ty < n && !f[tx][ty] && calc(tx) + calc(ty) <= k) 15 dfs(tx, ty, m, n, k); 16 } 17 } 18 int movingCount(int m, int n, int k) { 19 dfs(0, 0, m, n, k); 20 return ans; 21 } 22 };
其实还可以剪,对于 > k 的情况在 f 数组中标记个 -1,后面也无需再次判断,不过数据范围太小,影响较小。
20220310 剑指 Offer 14. 剪绳子
方法:数学推导
有意思的数学推导,简而言之就是:求 a = (n / m) ^ m 的最大值,可证明 m = n / e 时有最大值,即长度为 n / m = n / (n / e) = e。又 2 < e < 3,而 2 ^ (1/2) < 3 ^ (1/3),则切分长度为 3 时最优,其次为 2。
1 class Solution { 2 public: 3 int cuttingRope(int n) { 4 if (n <= 3) return n - 1; 5 int a = n / 3, b = n % 3; 6 return !b ? pow(3, a) : b == 1 ? pow(3, a - 1) * 4 : pow(3, a) * 2; 7 } 8 };
进阶:数据扩大到 n <= 1000,且对答案取余。需要用到大数取余。
方法一:暴力
已知 xy % p = (x % p) * (y % p) % p,故可以直接循环取余。
方法二:快速幂
1 const int MOD = 1e9 + 7; 2 3 class Solution { 4 public: 5 long long mypow(int a, int b) { 6 long long o = a, res = 1; 7 while (b) { 8 if (b & 1) (res *= o) %= MOD; 9 (o *= o) %= MOD; 10 b >>= 1; 11 } 12 return res; 13 } 14 int cuttingRope(int n) { 15 if (n <= 3) return n - 1; 16 int a = n / 3, b = n % 3; 17 return !b ? mypow(3, a) : b == 1 ? (mypow(3, a - 1) * 4) % MOD: (mypow(3, a) * 2) % MOD; 18 } 19 };
20220329 剑指 Offer 63. 股票的最大利润
1 class Solution { 2 public: 3 int maxProfit(vector<int>& a) { 4 int n = a.size(); 5 if (!n) return 0; 6 int mi = a[0], ans = 0; 7 for (int i = 1; i < n; i++) 8 if (a[i] < mi) mi = a[i]; 9 else ans = max(ans, a[i] - mi); 10 return ans; 11 } 12 };
122. 买卖股票的最佳时机 II
1 class Solution { 2 public: 3 int maxProfit(vector<int>& a) { 4 int n = a.size(), ans= 0; 5 for (int i = 1; i < n; i++) 6 ans += a[i] > a[i - 1] ? a[i] - a[i - 1]; 7 return ans; 8 } 9 };
123. 买卖股票的最佳时机 III
方法:动态规划
由于需要买卖两次,直接贪心找最优解不可取,这时候往往是考虑动态规划:
每天的可选状态很直白:没买;买了 1 次;买了 1 次又卖了;卖了 1 次后又买了第 2 次;买了第 2 次又卖了;
设 f[i][j] 为前 i 天可能获得的最大利润,状态的转移很直白:买第 i 天的股票即是在原基础上减去 price[i],卖则是加上;
而由于可以买多次,还需要加上表示已经买了 j 次的一维,那么就是设 f[i][j] 为前 i 天买了 j 次可能获得的最大利润;
当然这道题限定了 2 次,那么可以简单地设计成多个变量而无需加上一维。
1 class Solution { 2 public: 3 int maxProfit(vector<int>& a) { 4 int n = a.size(); 5 int b1 = -a[0], s1 = 0, b2 = -a[0], s2 = 0; 6 for (int i = 1; i < n; i++) { 7 b1 = max(b1, -a[i]), s1 = max(s1, b1 + a[i]); 8 b2 = max(b2, s1 - a[i]), s2 = max(s2, b2 + a[i]); 9 } 10 return s2; 11 } 12 };
124. 买卖股票的最佳时机 III
方法:动态规划
在第 123 题的基础上扩展为二维动态规划数组,用 b[i][j] 和 s[i][j] 分别表示第 i 天买/卖了第 j 次时获得的最大利润,思路同理。
1 typedef vector<int> arr; 2 typedef vector<arr> arr2; 3 4 class Solution { 5 public: 6 int maxProfit(int k, arr& a) { 7 if (a.empty()) return 0; 8 int n = a.size(); 9 k = min(k, n / 2); 10 arr2 b(n, arr(k + 1)); 11 arr2 s(n, arr(k + 1)); 12 b[0][0] = -a[0], s[0][0] = 0; 13 for (int i = 1; i <= k; ++i) 14 b[0][i] = s[0][i] = INT_MIN / 2; 15 for (int i = 1; i < n; ++i) { 16 b[i][0] = max(b[i - 1][0], s[i - 1][0] - a[i]); 17 for (int j = 1; j <= k; ++j) { 18 b[i][j] = max(b[i - 1][j], s[i - 1][j] - a[i]); 19 s[i][j] = max(s[i - 1][j], b[i - 1][j - 1] + a[i]); 20 } 21 } 22 return *max_element(s[n - 1].begin(), s[n - 1].end()); 23 } 24 };
20220409
144. 二叉树的前序遍历
方法一:递归法
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode() : val(0), left(nullptr), right(nullptr) {} 8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 10 * }; 11 */ 12 class Solution { 13 public: 14 vector<int> res; 15 vector<int> preorderTraversal(TreeNode* root) { 16 if (root == nullptr) 17 return res; 18 res.emplace_back(root->val); 19 preorderTraversal(root->left); 20 preorderTraversal(root->right); 21 return res; 22 } 23 };
方法二:迭代法
1 class Solution { 2 public: 3 vector<int> res; 4 vector<int> preorderTraversal(TreeNode* root) { 5 stack<TreeNode*> st; 6 TreeNode* node = root; 7 while (!st.empty() || node) { 8 while (node) { 9 st.push(node); 10 res.emplace_back(node->val); 11 node = node->left; 12 } 13 node = st.top(); 14 st.pop(); 15 node = node->right; 16 } 17 return res; 18 } 19 };
94. 二叉树的中序遍历
和前序遍历大同小异。
方法一:递归法
1 class Solution { 2 public: 3 vector<int> res; 4 vector<int> inorderTraversal(TreeNode* root) { 5 if (root == nullptr) 6 return res; 7 inorderTraversal(root->left); 8 res.emplace_back(root->val); 9 inorderTraversal(root->right); 10 return res; 11 } 12 };
方法二:迭代法
1 class Solution { 2 public: 3 vector<int> res; 4 vector<int> inorderTraversal(TreeNode* root) { 5 stack<TreeNode*> st; 6 TreeNode* node = root; 7 while (!st.empty() || node) { 8 while (node) { 9 st.push(node); 10 node = node->left; 11 } 12 node = st.top(); 13 res.emplace_back(node->val); 14 st.pop(); 15 node = node->right; 16 } 17 return res; 18 } 19 };
145. 二叉树的后序遍历
方法一:递归法
1 class Solution { 2 public: 3 vector<int> res; 4 vector<int> postorderTraversal(TreeNode* root) { 5 if (root == nullptr) 6 return res; 7 postorderTraversal(root->left); 8 postorderTraversal(root->right); 9 res.emplace_back(root->val); 10 return res; 11 } 12 };
方法二:迭代法
和前序、中序有不同,因为遍历只能自上而下遍历,但输出又需要自下而上输出,所以需要两个栈,分别用来遍历和输出。
1 class Solution { 2 public: 3 vector<int> res; 4 vector<int> postorderTraversal(TreeNode* root) { 5 stack<TreeNode*> st1, st2; 6 if (!root) return res; 7 TreeNode* node = root; 8 st1.push(root); 9 while (!st1.empty()){ 10 TreeNode* node = st1.top(); 11 st1.pop(); 12 st2.push(node); 13 if (node->left) st1.push(node->left); 14 if (node->right) st1.push(node->right); 15 } 16 while (!st2.empty()) { 17 res.push_back(st2.top()->val); 18 st2.pop(); 19 } 20 return res; 21 } 22 };
20220412
剑指 Offer 26. 树的子结构
1 class Solution { 2 public: 3 bool cmp(TreeNode* A, TreeNode* B) { 4 if (B == nullptr) return true; 5 if (A == nullptr || A->val != B->val) return false; 6 return cmp(A->left, B->left) && cmp(A->right, B->right); 7 } 8 bool isSubStructure(TreeNode* A, TreeNode* B) { 9 if (A == nullptr || B == nullptr) return false; 10 if (A->val == B->val && cmp(A, B)) 11 return true; 12 return isSubStructure(A->left, B) || isSubStructure(A->right, B); 13 } 14 };
20220416
剑指 Offer 17. 打印从1到最大的n位数
1 class Solution { 2 vector<int> ans; 3 public: 4 void add(string &s) { 5 int l = s.length() - 1; 6 s[l]++; 7 while (l) { 8 if (s[l] > '9') 9 s[l] = '0', s[l - 1]++; 10 l--; 11 } 12 } 13 void work(int o) { 14 string s = "1"; 15 for (int i = 1; i < o; i++) 16 s += '0'; 17 ans.push_back(stoi(s)); 18 int x = 9 * (int)pow(10, o - 1); 19 for (int i = 1; i < x; i++) 20 add(s), ans.push_back(stoi(s)); 21 } 22 vector<int> printNumbers(int n) { 23 for (int i = 1; i <= n; i++) 24 work(i); 25 return ans; 26 } 27 };
在 Dev-c++ 中编译选项加上了-std=c++11,但依旧不识别 stol 函数,不知道为什么。
1944. 队列中可以看到的人
方法:单调栈
1 class Solution { 2 public: 3 vector<int>canSeePersonsCount(vector<int>& a) { 4 int n = a.size(); 5 vector<int> ans(n); 6 stack<int> s; 7 for (int i = n - 1; i >= 0; --i) { 8 while (!s.empty()) { 9 ans[i]++; 10 if (a[i] > a[s.top()]) s.pop(); 11 else break; 12 } 13 s.push(i); 14 } 15 return ans; 16 } 17 };