括号问题
有效的括号 20
使括号有效的最小添加 921
平衡括号串的最小插入 1541
如果只判断一种括号,则不需要使用栈,通过一次遍历即可解决
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| bool isValid(string s){ int left = 0; for(char c : s){ if(c == '('){ left ++; } else if (left > 0){ left --; } else { return false; } } return left == 0; }
|
需要同时判断三种括号是否正确:() [] {}
使用上述方法就显得十分麻烦,应该利用栈结构的特性来解决
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #define IS_PUSH(x) (x == '(' || x == '[' || x == '{')
#define IS_MATCH(x, y) \ ((x == '(' && y == ')') || (x == '[' && y == ']') || (x == '{' && y == '}'))
class Solution { public: bool isValid(string s) { stack<char> stk; for (char c : s) { if (IS_PUSH(c)) { stk.push(c); } else { if (stk.empty()) return false; char c1 = stk.top(); if (IS_MATCH(c1, c)) { stk.pop(); } else { return false; } } }
return stk.empty(); } };
|
由于只涉及一种括号,就不需要使用栈结构,通过一次遍历即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int minAddToMakeValid(string s) { int left = 0; int cnt = 0; for (char c : s) { if (c == '(') { left++; } else if (left > 0) { left--; } else { cnt++; } } return left + cnt; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
int minInsertions1(string s) { int left = 0; int cnt = 0; int rightInRow = 0; for (char c : s) { if (c == '(') { if (rightInRow != 0) { if (left > 0) { cnt++; left--; } else { cnt += 2; } rightInRow = 0; } left++; } else { rightInRow++; if (rightInRow == 2) { if (left > 0) left--; else cnt++; rightInRow = 0; } } }
if (rightInRow != 0) { if (left == 0) { cnt += 2; } else { left--; cnt++; } rightInRow = 0; }
return left * 2 + cnt; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
int minInsertions(string s) { int need = 0, cnt = 0; for (char c : s) { if (c == '(') { if (need % 2 == 1) { need--; cnt++; } need += 2; } else { need--; if (need == -1) { cnt++; need = 1; } } }
return cnt + need; }
|
单调栈
单调栈实际上就是栈,只是通过简单的技巧使得其中元素的呈单调排列
可以很好解决【下一个更大元素】的问题
下一个更大元素Ⅰ 496
下一个更大元素Ⅱ 503
每日温度 739
给定一个数组 nums 返回一个结果数组 res,res[i] 表示 nums[i]
的下一个更大的元素,没有返回 -1
[2, 1, 2, 4, 3]
将每一个元素看成是相应高度的棍子。res[i]就是从nums[i]这根棍子向后所能看到的那个棍子的高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| vector<int> nextGreaterElement(vector<int> &nums){ stack<int> stk; vector<int> res(nums.size()); for(int i = nums.size() - 1; i >= 0; i--){ while(!stk.empty() && stk.top() <= nums[i]){ stk.pop(); } res[i] = stk.empty() ? -1 : stk.top(); stk.push(nums[i]); } return res; }
|
nums1
中数字 x
的
下一个更大元素 是指 x
在
nums2
中对应位置 右侧 的
第一个 比 x
大的元素。
这个问题更难了一些,因为是nums1
中的x
映射到nums2
后的下一个更大元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) { vector<int> res(nums1.size()); unordered_map<int, int> map;
stack<int> stk; for (int i = nums2.size() - 1; i >= 0; i--) {
while (!stk.empty() && stk.top() <= nums2[i]) { stk.pop(); }
map[nums2[i]] = stk.empty() ? -1 : stk.top();
stk.push(nums2[i]); }
for (int i = 0; i < nums1.size(); i++) { res[i] = map[nums1[i]]; }
return res; }
|
base case 的变式,res[i] 是 下一个最大元素 到 nums[i] 的距离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| vector<int> dailyTemperatures1(vector<int>& temperatures) { stack<pair<int, int>> stk; vector<int> res(temperatures.size()); for (int i = temperatures.size() - 1; i >= 0; i--) { while (!stk.empty() && stk.top().second <= temperatures[i]) { stk.pop(); } res[i] = stk.empty() ? 0 : stk.top().first - i; stk.push({i, temperatures[i]}); }
return res; }
vector<int> dailyTemperatures(vector<int>& temperatures) { stack<int> stk; vector<int> res(temperatures.size()); for (int i = temperatures.size() - 1; i >= 0; i--) { while (!stk.empty() && temperatures[stk.top()] <= temperatures[i]) { stk.pop(); } res[i] = stk.empty() ? 0 : stk.top() - i; stk.push(i); }
return res; }
|
引入了环形数组,但是可以通过模运算来模拟环形数组的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| vector<int> nextGreaterElements(vector<int>& nums) { stack<int> stk; vector<int> res(nums.size());
for (int i = 2 * nums.size() - 1; i >= 0; i--) { int j = i % nums.size(); while (!stk.empty() && stk.top() <= nums[j]) { stk.pop(); } if (i < nums.size()) { res[i] = stk.empty() ? -1 : stk.top(); } stk.push(nums[j]); }
return res; }
|
单调队列
滑动窗口最大值 239
【题目】给你一个整数数组 nums
,有一个大小为
k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的
k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
1 2 3 4 5 6 7 8 9 10 11
| 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
|
最主要是利用了【双端队列】,或者说是
利用可以在头部尾部增减元素的数据结构
难点在于:为什么窗口和队列中的元素可以不同步?
队列中的元素x,是在 比它更大元素y插入时【注意y是在x后面的】
被移出的。由于只是求窗口中的最大值,所以结果是不受影响的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int> res(nums.size() - k + 1); deque<int> dq; int cnt = 0; int index = 0; for(int i = 0; i < nums.size(); i++){
while(!dq.empty() && dq.back() < nums[i]){ dq.pop_back(); } dq.push_back(nums[i]); cnt++; if(cnt == k){ res[index++] = dq.front(); if(dq.front() == nums[i - k + 1]){ dq.pop_front(); } cnt --; } } return res; }
|
数组去重
去除重复字母 316
不同字符的最小子序列 1081
给你一个字符串 s
,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证
返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| string removeDuplicateLetters(string s) { int* cnts = new int[26]{0}; bool* isInStk = new bool[26]{false}; for (int i = 0; i < s.size(); i++) { cnts[s[i] - 'a']++; }
stack<char> stk; for (int i = 0; i < s.size(); i++) { cnts[stk.top() - 'a']--; if (isInStk[s[i] - 'a']) { continue; }
while (!stk.empty() && stk.top() >= s[i] && cnts[stk.top() - 'a'] > 1) { isInStk[stk.top() - 'a'] = false; stk.pop(); }
stk.push(s[i]); isInStk[s[i] - 'a'] = true; }
string ans = ""; while (!stk.empty()) { ans += stk.top(); stk.pop(); }
reverse(ans.begin(), ans.end());
return ans; }
string smallestSubsequence(string s) { int* cnts = new int[26]{0}; bool* isInStk = new bool[26]{false};
for (int i = 0; i < s.size(); i++) { cnts[s[i] - 'a']++; }
string stk; for (int i = 0; i < s.size(); i++) { if (isInStk[s[i] - 'a']) { cnts[s[i] - 'a']--; continue; }
while (!stk.empty() && stk.back() >= s[i] && cnts[stk.back() - 'a'] > 1) { isInStk[stk.back() - 'a'] = false; cnts[stk.back() - 'a']--; stk.pop_back(); }
stk.push_back(s[i]); isInStk[s[i] - 'a'] = true; }
return stk; }
|