常见模型:找出每个数左边离它最近的比它大/小的数 https://www.acwing.com/blog/content/404/
单调栈的运用找左边和右边第一个比它大的元素
题目1:找到左边比它小的,或者右边比它大的,有两种写法:for循环从大到小,for循环从小到大。
对于找右边的XXX,从N-1~0比较容易
对于找左边的XXX,从0~N-1比较容易
因为这样就不需要处理当没有元素的时候,栈里面还有元素的情况了。 https://www.acwing.com/problem/content/832/
#include
#include
#include
using namespace std;
int main(){
int N;
cin >> N;
vector m_vec(N);
for(int i = 0 ; i < N; i++){
int tmp;
cin >> tmp;
m_vec[i] = tmp;
}
//单调栈结构
vector res(N);
stack m_stack;
for(int i = 0 ; i < N; i++){
while(!m_stack.empty() && m_stack.top() >= m_vec[i]){
m_stack.pop();
}
res[i] = m_stack.empty() ? -1 : m_stack.top();
m_stack.push(m_vec[i]);
}
for(int i = 0 ; i < res.size(); i++){
cout << res[i] << " ";
}
}
#include
#include
using namespace std;
const int N = 1e5+10;
int arr[N];
//int st[N];
int n,m;
int main()
{
cin >> n;
stack st;
for(int i= 0;i < n;i++){
cin >> arr[i];
}
for(int i= n-1;i >= 0;i--){
while(!st.empty() && arr[i] < arr[st.top()] ){
int idx = st.top();
arr[idx] = arr[i];
st.pop();
}
st.push(i);
}
while(!st.empty()){
int idx = st.top();
arr[idx] = -1;
st.pop();
}
for(int i = 0; i < n;i++){
cout << arr[i] << ' ';
}
return 0;
}
作者:defddr
链接:https://www.acwing.com/solution/acwing/content/2248/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
上面那种单调栈只能处理右边第一个比它大的,如果题目想要同时知道左边第一个比它大的以及右边第一个比它大的,应该怎么做呢?后面发现是自己理解错了,实际上结构是一样的。。。。。
题目三连:
84. 柱状图中最大的矩形 https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
这题为了编程的方便,最重要的点就是加了0进去,或则是其他数来充当哑节点。
85. 最大矩形 https://leetcode-cn.com/problems/maximal-rectangle/
利用了上一个题目的思路,看不出来好吗.....https://leetcode-cn.com/problems/maximal-rectangle/
86. 数组中累积和与最小值的乘积, 假设叫做指标A。给定一个数组, 请返回子数组中, 指标A最大的值
首先要想到如何将题目进行转换?
要找到左边第一个小的,右边第一个比它小的。
84.
class Solution {
public:
int largestRectangleArea(vector& heights) {
//实际上这道题目就转换为寻找左边第一个比它小,右边第一个比它小
//维护一个递增序列
stack m_stack;
m_stack.push(-1);
int maxArea = 0;
for(int i = 0 ; i < heights.size(); i++){
while(m_stack.top()!=-1 && heights[m_stack.top()] > heights[i] ){
int id= m_stack.top();
int height = heights[id];
m_stack.pop();
int width = (i - m_stack.top() -1 );
maxArea = max(maxArea, height * width);
}
m_stack.push(i);
}
while(m_stack.top()!=-1){
int id = m_stack.top();
m_stack.pop();
int height = heights[id];
int width = (heights.size() - m_stack.top() - 1); //这里注意是heights.size()
maxArea = max(maxArea, height * width);
}
return maxArea;
}
};
这个做法非常棒,利用了最后一个元素0进行了一次清空的操作.
class Solution {
public:
/**
* @param height: A list of integer
* @return: The area of largest rectangle in the histogram
*/
int largestRectangleArea(vector &height) {
// write your code here
if(height.size() == 0) return 0;
int res = 0;
vector tmp = height;
tmp.push_back(0); // Important
stack s; //维护单调递增
for(int i = 0; i < tmp.size(); i++)
{
if(s.empty() || (!s.empty() && tmp[i] >= tmp[s.top()])) s.push(i); //如果当前高度大于栈顶,直接压入
else{
while(!s.empty() && tmp[s.top()] > tmp[i]) //如果栈顶高度大于当前高度
{
int idx = s.top(); s.pop(); //保存栈顶元素信息
int width = s.empty() ? i : (i-s.top()-1); //如果栈已经为空,宽度为i,否则i-s.top()-1
res = max(res, tmp[idx] * width);
}
s.push(i); // Important //压入栈中
}
}
return res;
}
};
85.
利用84题的程序
class Solution {
public:
int largestRectangleArea(vector& heights) {
//实际上这道题目就转换为寻找左边第一个比它小,右边第一个比它小
//维护一个递增序列
stack m_stack;
m_stack.push(-1);
int maxArea = 0;
for(int i = 0 ; i < heights.size(); i++){
while(m_stack.top()!=-1 && heights[m_stack.top()] > heights[i] ){
int id= m_stack.top();
int height = heights[id];
m_stack.pop();
int width = (i - m_stack.top() -1 );
maxArea = max(maxArea, height * width);
}
m_stack.push(i);
}
while(m_stack.top()!=-1){
int id = m_stack.top();
m_stack.pop();
int height = heights[id];
int width = (heights.size() - m_stack.top() - 1); //这里注意是heights.size()
maxArea = max(maxArea, height * width);
}
return maxArea;
}
int maximalRectangle(vector>& matrix) {
if(matrix.size() == 0 || matrix[0].size() ==0) return 0;
vector heights(matrix[0].size());
int maxArea = 0;
for(int i = 0 ; i < matrix.size(); i++){
for(int j = 0 ; j < matrix[0].size(); j++){
if(matrix[i][j] == '1'){
heights[j]++;
}else{
heights[j] = 0;
}
}
maxArea = max(maxArea, largestRectangleArea(heights));
}
return maxArea;
}
};
滑动窗口题目模板:
下面给出了单调队列的模板,当然是labuladong大佬写得。不过这个模板如果要事先要声明一个单调队列的话,存在一些问题,就是不能存储下标值,这样的话算法的通用性不是很高。
class MonoQueue{
private:
deque m_queue;
public:
void push(int n){
//再遇到比你厉害的人之前一直弹出,所以得到了一个递减序列
while(!m_queue.empty() && m_queue.back() < n){
m_queue.pop_back();
}
m_queue.push_back(n);
}
int max() { return m_queue.front();}
void pop(int n){
//按理来说直接pop就可以,为什么需要m_queue.front() 呢?
//因为m_queue在push的时候可能已经将元素n给删除了
if(!m_queue.empty() && m_queue.front() == n){
m_queue.pop_front();
}
}
};
class Solution {
public:
vector maxSlidingWindow(vector& nums, int k) {
if(nums.size() < k || nums.size() == 0) return vector();
MonoQueue m_queue;
vector res;
for(int i = 0 ; i < nums.size(); i++){
if(i < k -1){
m_queue.push(nums[i]);
}else{
m_queue.push(nums[i]);
res.push_back(m_queue.max());
m_queue.pop(nums[i-k+1]);
}
}
return res;
}
};
如果将此题改成按标号的话,会发现代码比较复杂,
class Solution {
public:
vector maxSlidingWindow(vector& nums, int k) {
if(nums.size() < k || nums.size() == 0) return vector();
deque m_queue;
vector res;
for(int i = 0 ; i < nums.size(); i++){
if(i < k - 1){
while(!m_queue.empty() && nums[m_queue.back()] < nums[i]){
m_queue.pop_back();
}
m_queue.push_back(i);
}else{
while(!m_queue.empty() && nums[m_queue.back()] < nums[i]){
m_queue.pop_back();
}
m_queue.push_back(i);
res.push_back(nums[m_queue.front()]);
if( (i-k+1) == m_queue.front()){
m_queue.pop_front();
}
}
}
return res;
}
};
那还不如直接以后遇到类似题目就使用下面这个模板呢~~~~
class Solution {
public:
vector maxSlidingWindow(vector& nums, int k) {
vector res;
deque q;
for(int i=0;i=k-1)res.push_back(nums[q.front()]);
}
return res;
}
};
作者:炼心
链接:https://www.acwing.com/blog/content/759/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。