博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单调栈
阅读量:6229 次
发布时间:2019-06-21

本文共 3960 字,大约阅读时间需要 13 分钟。

  单调栈是栈中的元素从栈顶到栈底单调递增或递减。单调栈只能从栈顶部添加或者删除元素,以单调递增栈为例,元素从栈顶到栈底逐渐递增,假设当前元素为e,在入栈时,从栈顶往下找,如果栈顶元素小于当前元素e,则弹出栈顶元素,直到栈顶元素大于等于该元素e,然后将e入栈;出栈时,直接弹出栈顶元素,即可得到栈中当前所有元素的最小值。

  由于每个元素都会入栈和出栈一次,因此平摊下来,时间复杂度为O(n).

单调栈示例: poj 2559 Largest Rectangle in a Histogram

  题目要求求出由给定的整数数组确定的直方图中,连续的一块最大的矩形面积,其中每个直方列的宽度均为1。

题目解法:

  将关注点放在直方图的每个列上,由该列的高度确定的一个矩形(从该点的峰点出发,分别向左和向右延伸,得到最大的宽度,从而得到由该高度确定的矩形的面积)。然后进行一次遍历,得到所有点确定的矩形中最大的一个。

   从一点向左(或右)延伸的最大长度(以向左延伸为例),就是从该点出发向左走,如果左边点的高度大于等于该点,则长度加1, 直到遇到的点的高度小于该点的高度则停止,得到最大值。如果使用纯粹的搜索方法,对每个点都做一遍“从该点开始,向左走直到遇到小于它高度的点” 则,复杂度为 O(n^2),仔细分析可以发现: 如果 进行完 Ai的搜索,得到高度小于Ai的点为P,在进行Ai+1的 搜索的时候,如果 h[i+1] > h[i], 则直接得到结果i;如果h[i+1] < h[i],则 从P到i之间的点的高度 肯定大于h[i+1](因为它们大于h[i]),所以只需要从P开始接着往前找即可。 这样的搜索过程,可以简化为在一个单调递减的栈中进行查找,这个过程可以用单调栈来实现。 在实现的时候,考虑求该点向左延伸时,左边最小的点的索引,并且将索引用栈来存储。

代码实现:注意 long long 的类型,以及 long long result = (right[i] - left[i] + 1)*hist[i]; (在hist 为整数数组时候,需要强制转换为 long long)

 不使用单调栈!

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;const int kMax = 100005;/*庞大的数组,从小处入手 =_+, 考虑从直方图中每个位置向左和向右扩展,可以得到的以当前位置处高度为最低高度的序列长度w。这样,可以求出从位置i,分别向左向右扩展,保持矩形高度为height[i],得到的矩形面积 为height[i]*w最后遍历 i,求出最大值。对于每个位置i,我们想要知道它最左边的高度比它的高度低的位置,以及最右边的高度比它的高度低的位置。最naive的方法是对每个i都向左和向右扫描,找到第一个比height[i] 小的位置j,k。这种方法中会有很多重复计算:假设 位置 i > j > k现在考虑 k 向左扩展能够得到的最大长度,此时向左扫描到了j,如果 height[j] < height[k],此时扫描可以结束;如果 height[j] >= height[k]继续向左扫描,而之前已经计算过了j向左扩展得到的最大长度,假设j左边的位置 i处 height[i] >= height[j], 那么height[i] >= height[k],在从j向左扫描的时候,路过了i,现在从k向左扫描,还要再路过i一次!!!! 出现了重复。那么,如何消除呢?在j向左扫描的时候,获得了j左边第一个height[p]比height[j]小的位置p,那么再从k向左扫描的时候,从j,可以直接跳到p....核心想法就是将每个位置的左边第一个比该位置处高度小的那个点记录下来,之后直接跳到那个点。*///height 数组int height[kMax];// left_less_index[i] 表示height数组中从i向左查找,height 第一个比 height[i] 小的位置int left_less_index[kMax];// right_less_index[i] 表示height数组中从i向右查找,height 第一个比 height[i] 小的位置int right_less_index[kMax];long long int solve(int n){ int j; for (int i = 0; i < n; i++){ j = i - 1; while (j >= 0 && height[j] >= height[i]){ j = left_less_index[j]; } left_less_index[i] = j; } for (int i = n - 1; i >= 0; i--){ j = i + 1; while (j < n && height[j] >= height[i]){ j = right_less_index[j]; } right_less_index[i] = j; } long long result = 0; for (int i = 0; i < n; i++){ long long tmp = ((long long)height[i]) * (right_less_index[i] - left_less_index[i] - 1); result = max(tmp, result); } return result;}int main(){ int n; while (scanf("%d", &n)){ if (n == 0) break; for (int i = 0; i < n; i++){ scanf("%d", &height[i]); } long long int result = solve(n); printf("%lld\n", result); } return 0;}

 使用单调栈!

/*除了用数组 left_less_index, right_less_index 记录下来每个位置i处左边/右边第一个高度比height[i]小的位置之外,还可以使用单调栈。栈中存放的是 height数组中的索引【注意,单调栈和单调队列中存放的都是数组元素的索引】。每次计算完位置i,栈顶元素就更新为 i。每次计算的时候,都从栈顶向栈底寻找第一个height[index] 比height[i]小的index, 而将中间过程中的那些不满足条件的index都弹出栈。*///height 数组int height[kMax];//单调栈,栈中存放height数组的索引,从栈顶到栈底 栈中元素(索引)所在位置的height单调递减int mon_stack[kMax];//left_width[i] 表示i处的元素可以向左扩展的最大长度,不包括iint left_width[kMax];//right_width[i] 表示i处的元素可以向右扩展的最大长度,不包括iint right_width[kMax];long long int solve(int n){	int top = -1;	for (int i = 0; i < n; i++){		while (top >= 0 && height[mon_stack[top]] >= height[i]){			top--;		}		left_width[i] = top < 0? i: i - mon_stack[top] - 1;		mon_stack[++top] = i;	}	top = -1;	for (int i = n - 1; i >= 0; i--){		while (top >= 0 && height[mon_stack[top]] >= height[i]){			top--;		}		right_width[i] = top < 0? n - i - 1: mon_stack[top] - i - 1;		mon_stack[++top] = i;	}	long long result = 0;	for (int i = 0; i < n; i++){		long long tmp = ((long long)height[i]) * (left_width[i] + right_width[i] + 1);		result = max(tmp, result);	}	return result;}int main(){	int n;	while (scanf("%d", &n)){		if (n == 0)			break;		for (int i = 0; i < n; i++){			scanf("%d", &height[i]);		}		long long int result = solve(n);		printf("%lld\n", result);	}	return 0;}

 

转载地址:http://ryxna.baihongyu.com/

你可能感兴趣的文章
标签制作软件中如何导出标签模板为PDF文件?
查看>>
Linux运维系统工程师系列---22
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
域结构的网络
查看>>
mysql 命令
查看>>
Oracle 11g rac 生产环境部署详录
查看>>
web.xml 中<taglib>报错
查看>>
Linux文件系统上的特殊权限(SUID、SGID、Sticky)的知识点
查看>>
零部件表设计 T_AIS_BASE_PARTS_INFO
查看>>
fgsdf
查看>>
一、Asp.Net MVC4.0开发CMS系统案例之数据库设计
查看>>
Vue.js 2.x笔记:路由Vue Router(6)
查看>>
HTTP请求对消息主体进行编码的方法
查看>>
归并排序以及逆序数计算
查看>>
jQuery 下拉列表 二级联动插件
查看>>
jQuery的样式篇
查看>>
QT(4)信号与槽
查看>>
(转)jieba中文分词的.NET版本:jieba.NET
查看>>
PHP 反射机制
查看>>