• 【单调栈】视野总和、柱状图中最大的矩形、区间最大值


    单调栈

    • 单调递增栈:数据出栈的序列为单调递增序列(比站内元素小就入栈,否则将栈中比当前元素小的元素弹出后再入栈)
    • 单调递减栈:数据出栈的序列为单调递减序列(比站内元素大就入栈,否则将栈中比当前元素大的元素弹出后再入栈)

    视野总和

    描叙:有n个人站队,所有的人全部向右看,个子高的可以看到个子低的发型,给出每个人的身高,问所有人能看到其他人发现总和是多少。
    输入:4 3 7 1
    输出:2
    思路:设置一个单调递增栈,当这个人可以被站内人看到时(即比栈内数小时)入栈,同时将栈内每个人能看到的人数加一,如果遇到了一个比栈顶元素高的人,那么栈内一些人是看不到他的,所以将这些看不到他的人出栈,出栈完成后再将栈内剩下元素能看到的人数各自加一。大致是这种思路,但是由于这题求得是总和,所以不必求出每个人能看到多少人,避免每次有元素入栈都要遍历原有栈内的元素。可以改为在每个元素出栈时计算他能看到的人数。例如第i个人要入栈(即第i个人比s.top()这个人高时),此时栈内s.top()这个人被第i个人挡住了,第i个人后面的都看不到了,所以其出栈时看到的人数为i~s.top()之间的人。

    #include<bits/stdc++.h>
    using namespace std;
    
    int FieldSum(vector<int>& v)
    {
    	int sum=0;
    	v.push_back(INT_MAX);
    	stack<int> s;
    	for(int i=0;i<v.size();i++)
    	{
    		while(!s.empty()&&v[i]>v[s.top()])
    		{
    			int x=s.top();
    			s.pop();
    			sum+=(i-x-1);   //i为当前元素,x为出栈元素,相减部分为x可以看到的数量;
    		}
    		s.push(i);
    	}
    	return sum;
    }
    int main()
    {
    	int a[6]={8,3,2,7,1,4};
        vector<int> v;
        for(int i=0;i<6;i++)
        {
        	v.push_back(a[i]);
    	} 
    	cout<<FieldSum(v)<<endl;
    	return 0;
    }
    

    柱状图中最大

    描述
    Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1,
    find the area of largest rectangle in the histogram.

    1584440534718

    图 4-1 Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

    1584440545597

    图 4-2 The largest rectangle is shown in the shaded area, which has area = 10 unit.
    For example, Given height = [2,1,5,6,2,3], return 10.

    单调栈

    理解:当遍历到第i个数时,会算出所有向左看时在数i前即包含第(i-1)个数的最大值,假设这个最大值矩形左边边界为left,右边为right(即此时right+1大于left-1),这种情况下,以左边界[left]>[right+1]但是[left-1]<[right+1],所以 这里的大小关系也是出栈的依据,以此来计算宽度,h[right]即为这一段上的最小值。

    #include<bits/stdc++.h>
    using namespace std;
    //#define MAX 10005
    
    int  largestRectangleArea(vector<int>& heights)
    {
    		stack<int> s;
    	int max=0;
    	heights.push_back(0);      //用于处理最后一个数据 
    	int len=heights.size();
    	for(int i=0;i<len;i++)
    	{
    		while(!s.empty()&&heights[s.top()]>heights[i])
    		{
    			int x=s.top();
    			s.pop();
                //temp为宽度,如果s为空,则说明,x之前的元素都比x高,所以均可计入,宽度即为(i-1-0+1)
                //s为空时,两边的边界依次为下标为0,下标为i-1;
    			int temp=heights[x]*(s.empty()?i:(i-s.top()-1));
    			max=max>temp?max:temp;
    		}
    		s.push(i);
    	}
    	
    	return max;
    }
    
    
    int main()
    {
        int a[6]={2,1,5,6,2,3};
        vector<int> v;
        for(int i=0;i<6;i++)
        {
        	v.push_back(a[i]);
    	} 
    	cout<<largestRectangleArea(v)<<endl;
    	return 0;
        	
    }
    

    分治法

    此题还可以使用分治法,记左右边界为left,right,可以先找出一个序列中的最小值min,若最大矩形包含这个min,则最大矩形一定为(right-left+1)*min;所以可以分为(left,min-1),(min+1,right)两个子问题寻找最大矩形,然后三种情况取最大值。(因为最大矩形连续,而第二三种情况一定不包含min,所以可以分开看成两个无联系的子问题)

    int maxArea(int left,int right,vector<int>& v)
    {
    	if(left==right)
    		return v[left];
    	bool sortedlr=true;               //是否从左到右递增 
    	bool sortedrl=true;               //是否从右到左递增 
    	int min=left;
    	
    	for(int i=left;i<=right;i++)
    	{
    		if(v[i]>v[i-1]) 
    		{
    			sortedrl=false;
    		}
    		if(v[i]<v[i-1])
    		{
    			sortedlr=false;
    		}
    		if(v[i]<v[min])
    		{
    			min=i;
    		}
    	}
    	if(sortedlr)
    	{
    		int maxa=v[left]*(right-left+1);
    		
    		for(int i=left+1;i<=right;i++)
    		{
    			int temp=v[i]*(right-i+1);
    			maxa=maxa>temp?maxa:temp;
    		}
    		return maxa;
    		
    	}
    	if(sortedrl)
    	{
    		int maxa=v[right]*(right-left+1);
    		for(int i=right-1;i>=left;i--)
    		{
    			int temp=v[i]*(i-left+1);
    			maxa=maxa>temp?maxa:temp;
    		}
    		return maxa;
    	}
    	else
    	{
    		//继续分治
    		int l=0;
    		if(left<min)
    		{
    			l=maxArea(left,min-1,v);
    		}
    		int r=0;
    		if(right>min)
    		{
    			r=maxArea(min+1,right,v);
    		} 
    		int x=l>r?l:r;
    		int temp=v[min]*(right-left+1);
    		return x>temp?x:temp;
    	}
    } 
    
    
    int largestRectangleArea(vector<int>& heights)
    {
    	int left=0;
    	int right=heights.size()-1;
    	if(right==-1)
    	return 0;
    	else if(right==0)
    	{
    		return heights[0];
    	}
    	else 
    	{
    		return maxArea(left,right,heights);
    	}
    } 
    

    求最大区间

    描述:给出一组数字,求一区间,使得区间元素和乘以区间最小值最大,结果要求给出这个最大值和区间的左右端点
    输入:3 1 6 4 5 2
    输出:60
           3 5
    解释:将3到5(6+4+5)这段区间相加,将和与区间内最小元素相乘获得最大数字60
    思路:使用暴力解法求出所有区间,再求出区间的最小值相乘跟新数据,并不是一种很好的算法,所以经过上面俩题的磨炼,此时我们应该使用一个单调递减栈同矩形相似,如果以一个数为该区间最小值,则算出该最小值可以构成的最大区间。

    #include<bits/stdc++.h>
    using namespace std;
    
    int findMax(vector<int>& v,int &p,int &q)
    {
    	v.push_back(0);
    	stack<int> s;
    	int max=0;
    	int sum;
    	
    	for(int i=0;i<v.size();i++)
    	{
    		while(!s.empty()&&v[s.top()]>v[i])
    		{
    			int x=s.top();
    			s.pop();
    			sum=0;
    			int j;
    			if(s.empty())
    			{
    				//p=0;
    				j=0;
    			}
    			else
    			{
    				//p=s.top()+1;
    				j=s.top()+1;
    			}
    				
    			for(j;j<i;j++)
    			{
    				sum+=v[j];
    			}
    			
    			
    			int temp=sum*v[x];
    			max=max>temp?max:temp;
    			if(max==temp)
    			{
    				p=s.empty()?0:s.top()+1;
    				q=i-1;
    			}
    		}
    		s.push(i);
    	}
    	return max;
    }
    
    int main()
    {
    	int a[]={3,1,6,4,5,2};
    	vector<int> v;
    	int p,q;
    	for(int i=0;i<6;i++)
    	{
    		v.push_back(a[i]);
    	}
    	int x=findMax(v,p,q);
    	cout<<x<<endl;
    	cout<<(p+1)<<" "<<(q+1)<<endl;       //p,q,为下标,输出为序号 
    	return 0;
    }
    

    寻找无序数组每个元素的后面第一个比它大的元素值

    如题

    #include<bits/stdc++.h>
    using namespace std;
    vector<int> nextmax(vector<int> &v)
    {
    	stack<int> s;
    	vector<int> res(v.size());
    	
    	int size=v.size();
    	int i=0;
    	while(i<size)
    	{
    		if(s.empty()||v[s.top()]>=v[i])
    		{
    			s.push(i++); 
    		}
    		else
    		{
    			int tmp=s.top();
    			res[tmp]=v[i];
    			s.pop();
    		}
    	}
    	while(!s.empty()) 
    	{
    		res[s.top()]=INT_MAX;         //将后面没有比他更大的元素置为INT_MAXINT_MAX
    		s.pop();
    	}
    	return res;
    }
    int main()
    {
    	int a[10]={1,2,4,2,5,76,89,3,45,34};
    	vector<int> v;
    	for(int i=0;i<10;i++)
    	{
    		v.push_back(a[i]);
    	}
    	vector<int> res=nextmax(v);
    	for(int i=0;i<res.size();i++)
    	{
    		cout<<res[i]<<endl;
    	}
    	return 0;
    	
    } 
    
  • 相关阅读:
    使企点QQ来消息时不自动弹出窗口(以避免错过旧的消息和避免正在回复A时自动定位到B从而影响客服效率)
    8款开源聊天软件
    博客园设置皮肤(宽屏等)及选择文本编辑器
    Windows Server 2019 设置使用照片查看器查看图片
    使windows10重启后打开上次的文件夹和程序
    TinyMCE 5 富文本编辑器好
    wordpress 安装 Elementor PRO 插件(破解版)
    无法打印,提示“windows 打印后台处理程序 没有运行”的解决方案
    第一次将代码提交至git error: failed to push some refs to 'https://gitee.com/xxx/test.git'
    git本地和远程的关联问题
  • 原文地址:https://www.cnblogs.com/wwj321/p/12515637.html
Copyright © 2020-2023  润新知