例:求左边第一个比当前数小的数
维护一个单调递增栈,如果i
#include
using namespace std;
const int N = 100010;
int stk[N], tt;
int main() {
int n;
int x;
scanf("%d", &n);
while (n--) {
cin >> x;
while (stk[tt] >= x) tt--;
if (tt) cout << stk[tt] << " ";
else cout << -1 << " ";
stk[++tt] = x;
}
return 0;
}
题目:请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
思路:模板题,找右边第一个大于当前元素的位置,栈存储位置即可。
答案:
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& t) {
vector<int> res(t.size());
stack<int> st;
for (int i = t.size() - 1; i >= 0; i--) {
while (st.size() && t[i] >= t[st.top()]) st.pop();
if (st.size()) res[i] = st.top() - i;
st.push(i);
}
return res;
}
};
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int, int> hash; //用unordered_map哈希表来存,降低时间复杂度
vector<int> res;
stack<int> st;
for (int i = nums2.size() - 1; i >= 0; i--) {
int cur = nums2[i]; //该循环中反复使用的数,存下来降低时间复杂度
while(st.size() && st.top() <= cur) st.pop();
if(st.size()) hash[cur] = st.top();
else hash[cur] = -1;
st.push(cur);
}
for (int i = 0; i < nums1.size(); i++) {
res.push_back(hash[nums1[i]]);
}
return res;
}
};
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
stack<int> st;
int n = nums.size();
vector<int> res(n);
nums.insert(nums.end(), nums.begin(), nums.end());
for (int i = nums.size() - 1; i >= 0; i--) {
int cur = nums[i];
while (st.size() && cur >= st.top()) st.pop();
if (i < n) { //遍历到对应元素再记录
if (st.empty()) res[i] = -1;
else {
res[i] = st.top();
}
}
st.push(cur);
}
return res;
}
};
class StockSpanner {
public:
StockSpanner() {
}
stack<pair<int, int>> st;
int next(int price) {
int w = 1;
while(!st.empty() && st.top().first <= price) {
w += st.top().second;
st.pop();
}
st.push(pair<int, int>(price, w));
return w;
}
};
题目:给出一个以头节点 head 作为第一个节点的链表。链表中的节点分别编号为:node_1, node_2, node_3, … 。
每个节点都可能有下一个更大值(next larger value):对于 node_i,如果其 next_larger(node_i) 是 node_j.val,那么就有 j > i 且 node_j.val > node_i.val,而 j 是可能的选项中最小的那个。如果不存在这样的 j,那么下一个更大值为 0 。
返回整数答案数组 answer,其中 answer[i] = next_larger(node_{i+1}) 。
思路:我是先把链表存成了vector,然后用传统模板做的,但是似乎有遍历一遍的做法,空间复杂度会更低。到时候看Y总会不会讲啦啦啦。
答案:
class Solution {
public:
vector<int> nextLargerNodes(ListNode* head) {
stack<int> st;
vector<int> res;
ListNode* cur = head;
while(cur->next != NULL) {
res.push_back(cur->val);
cur = cur->next;
}
res.push_back(cur->val);
vector<int> ans(res.size(), 0);
for (int i = res.size() - 1; i >= 0; i--) {
while (!st.empty() && res[i] >= st.top()) st.pop();
if (!st.empty()) ans[i] = st.top();
st.push(res[i]);
}
return ans;
}
};
题目:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
思路:枚举每条边做上边界的情况,找出每条柱子左边和右边第一个低于它的柱子,算出它做上边界的面积。可以做常数优化:不用遍历两遍,在求左边界的同时,如果某条边出栈,意味着右边出现了第一个小于等于它的柱子,虽然我们想要的是小于它的柱子,但是在下一个等于它的柱子处可以求得它本身的正确答案,所以是正确的。
答案:
遍历两遍
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> st1;
int n = heights.size();
vector<int> res1(n), res2(n);
for (int i = 0; i < heights.size(); i++) {
while(!st1.empty() && heights[i] <= heights[st1.top()]) st1.pop();
if(!st1.empty()) res1[i] = st1.top();
else res1[i] = -1;
st1.push(i);
}
st1 = stack<int>();
for (int i = n - 1; i >= 0; i--) {
while(!st1.empty() && heights[i] <= heights[st1.top()]) st1.pop();
if(!st1.empty()) res2[i] = st1.top();
else res2[i] = n;
st1.push(i);
}
int m = 0;
for (int i = 0; i < heights.size(); i++) {
m = max(m, heights[i] * (res2[i] - res1[i] - 1));
}
return m;
}
};
优化:虽然是常数优化,但是时间复杂度排名一下子提到90%以上了。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> st1;
int n = heights.size();
vector<int> res1(n, -1), res2(n, n);
for (int i = 0; i < heights.size(); i++) {
while(!st1.empty() && heights[i] <= heights[st1.top()]) {
res2[st1.top()] = i;
st1.pop();
}
if(!st1.empty()) res1[i] = st1.top();
st1.push(i);
}
int m = 0;
for (int i = 0; i < heights.size(); i++) {
m = max(m, heights[i] * (res2[i] - res1[i] - 1));
}
return m;
}
};
题目:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
思路:我开始又找到了两边高于当前柱子的位置,然后枚举每条边当下边界的情况算,总的来说需要考虑的事情多一些,而且需要遍历两遍。只遍历一遍的最优解是出现凹槽的右端点再加上这段面积,和5一样,利用的原理是:当一个数出栈时说明找到了右边大于当前的数,这里注意没有取等号。
答案:
class Solution {
public:
int trap(vector<int>& h) {
stack<int> st1;
int n = h.size();
if (n == 0) return 0;
int ans = 0;
for (int i = 0; i < n; i++) {
while(!st1.empty() && h[i] > h[st1.top()]) {
int bot = st1.top();
st1.pop();
if (st1.empty()) break;
int left = st1.top();
ans = ans + (min(h[left], h[i]) - h[bot]) * (i - left - 1);
}
st1.push(i);
}
return ans;
}
};
题目:给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
思路:枚举每条边做下边界,以这条边界为底算柱状图中的最大矩形(利用LeetCode84);最后比较各条下边界做底的最大值。
答案:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> st1;
int n = heights.size();
vector<int> res1(n, -1), res2(n, n);
for (int i = 0; i < heights.size(); i++) {
while(!st1.empty() && heights[i] <= heights[st1.top()]) {
res2[st1.top()] = i;
st1.pop();
}
if(!st1.empty()) res1[i] = st1.top();
st1.push(i);
}
int m = 0;
for (int i = 0; i < heights.size(); i++) {
m = max(m, heights[i] * (res2[i] - res1[i] - 1));
}
return m;
}
int maximalRectangle(vector<vector<char>>& matrix) {
if (matrix.empty() || matrix[0].empty()) return 0;
int n = matrix.size(), m = matrix[0].size();
vector<vector<int>> h(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (matrix[i][j] == '1') {
if (i) {
h[i][j] = 1 + h[i - 1][j];
}
else h[i][j] = 1;
}
}
}
int res = 0;
for (int i = 0; i < n; i++) {
res = max(res, largestRectangleArea(h[i]));
}
return res;
}
};
题目:给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
思路:在去除k个数字之前,维护单调栈递增,直到已经删除k个数字;如果最后长度多于len-k个数字,只取前len-k个数字;记得去掉结果的所有前导零,在长度大于1的时候。如果删除长度等于字符串本身长度,直接返回0.
答案:
class Solution {
public:
string removeKdigits(string num, int k) {
if (num.size() == k) return "0";
vector<int> stk;
int targetLen = num.size() - k;
for (int i = 0; i < num.size(); i++) {
while (stk.size() && stk.back() > num[i] && k) {
stk.pop_back();
k--;
}
stk.push_back(num[i]);
}
string res;
for (int i = 0; i < targetLen; i++) {
res += stk[i];
}
while (res[0] == '0' && res.size() > 1) res = res.substr(1);
return res;
}
};
class Solution {
public:
string removeDuplicateLetters(string s) {
unordered_map<char, int> hash;
for (int i = 0; i < s.size(); i++) {
if (hash.count(s[i])) hash[s[i]]++;
else hash[s[i]] = 1;
}
string stk;
unordered_set<char> map;
for (int i = 0; i < s.size(); i++) {
if (map.count(s[i])) {
hash[s[i]] -= 1;
continue;
}
while (stk.size() && s[i] < stk.back() && hash[stk.back()] > 1) {
hash[stk.back()] -= 1;
map.erase(stk.back());
stk.pop_back();
}
map.insert(s[i]);
stk += s[i];
}
return stk;
}
};
题目:给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。
说明: 请尽可能地优化你算法的时间和空间复杂度。
思路:枚举两个数组中取数的个数,使其加起来为k;分别求两个数组中取i和j个数最大的取法;最后按照一定顺序合并;比较每种枚举方法拼接结果的大小。
答案:
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
vector<int> res;
if (k == nums1.size() + nums2.size()) return merge(nums1, nums2); //没有选择余地
//枚举从两个数组中取数的个数
for (int i = 0; i <= min(int(nums1.size()), k); i++) {
int j = k - i;
if (j <= min(int(nums2.size()), k)) {
//贪心:两个数组中的最大拼接起来就是全局最大
auto a = getMaxNum(nums1, i);
auto b = getMaxNum(nums2, j);
auto temp = merge(a, b);//合并
//更新
if (!res.size()) res = temp;
else if (compare(res, 0, temp, 0) <= 0) res = temp;
}
}
return res;
}
//调用lc402的结果计算给定数组中选k位最大的数
vector<int> getMaxNum(vector<int>& nums, int k) {
vector<int> stk;
int popNum = nums.size() - k;
for (int i = 0; i < nums.size(); i++) {
while (popNum && stk.size() && nums[i] > stk.back()) {
stk.pop_back();
popNum--;
}
stk.push_back(nums[i]);
}
while (popNum) {
stk.pop_back();
popNum--;
}
return stk;
}
//难点在于两个vector并非一定是严格降序的,因此需要比较两个数剩余部分的大小来决定先拼接谁
vector<int> merge(vector<int>& a, vector<int>& b) {
int i = 0, j = 0;
vector<int> res;
while (i < a.size() || j < b.size()) {
if (compare(a, i, b, j) > 0) {
res.push_back(a[i++]);
}
else {
res.push_back(b[j++]);
}
}
return res;
}
//比较两个vector某个index之后的部分大小
int compare(vector<int>& a, int index1, vector<int>& b, int index2) {
int i = 0;
while (index1 < a.size() && index2 < b.size()) {
int difference = a[index1++] - b[index2++];
if (difference != 0) return difference;
}
//跳出循环是因为某个vector已经遍历到末尾,那么谁还剩下数谁优先
return (a.size() - index1) - (b.size() - index2);
}
};