import java.util.Stack;
public class LargestRectangleInHistorgram {
//方法一:暴力法(遍历所以可能的宽度)
public int largestRectangleArea1(int[] heights) {
//定义变量保存最大面积
int largestArea= 0;
//遍历数组,作为矩形左边界
for (int left = 0; left <heights.length ; left++) {
//定义变量保存当前矩形高度
int currHeight = heights[left];
//遍历数组,选取矩形右边界
for (int right = left;right<heights.length;right++){
//确定 当前矩形高度
currHeight = (heights[right]<currHeight)?heights[right]:currHeight;
//计算当前矩形面积
int currArea = (right-left+1)*currHeight;
//更新最大面积
largestArea = (currArea>largestArea)?currArea:largestArea;
}
}
return largestArea;
}
//方法二:双指针(遍历所以可能的高度)
public int largestRectangleArea2(int[] heights) {
//定义变量保存最大面积
int largestArea= 0;
//遍历数组,以每个柱子高度作为最终矩形的高度
for (int i = 0; i <heights.length ; i++) {
//保存当前高度
int height = heights[i];
//定义左右指针
int left = i;
int right = i;
//寻找左右边界,左指针左移
while(left>=0){
if(heights[left]<height){
break;
}
left--;
}
//寻找左右边界,右指针右移
while(right<heights.length){
if(heights[right]<height){
break;
}
right++;
}
//计算宽度
int width = right-left-1;
//计算面积
int currArea = height*width;
largestArea = currArea>largestArea?currArea:largestArea;
}
return largestArea;
}
//方法三:双指针法改进
public int largestRectangleArea3(int[] heights) {
//定义变量保存最大面积
int largestArea= 0;
//定义两个数组,保存每个柱子对应的左右边界
int n = heights.length;
int[] lefts = new int[n];
int[] rights = new int[n];
//遍历数组,计算左边界
for (int i = 0; i <n ; i++) {
//保存当前高度
int height = heights[i];
//定义左右指针
int left = i -1;
//左指针左移,寻找左边界
//寻找左右边界,左指针左移
while(left>=0){
if(heights[left]<height){
break;
}
left = lefts[left];//如果左边柱子更高,就直接跳到左边界柱子判断
}
lefts[i]= left;
}
//遍历数组,计算右边界
for (int i = n-1; i >=0 ; i--) {
//保存当前高度
int height = heights[i];
//定义左右指针
int right = i+1;
//右指针右移,寻找右边界
while(right<n){
if(heights[right]<height){
break;
}
right = rights[right];//如果左边柱子更高,就直接跳到左边界柱子判断
}
rights[i]= right;
}
//遍历所有柱子计算面积
for (int i = 0; i <n ; i++) {
int currArea = (rights[i]-lefts[i]-1)*heights[i];
largestArea = currArea>largestArea?currArea:largestArea;
}
return largestArea;
}
//方法四:单调栈
public int largestRectangleArea4(int[] heights) {
//定义变量保存最大面积
int largestArea= 0;
//定义两个数组,保存每个柱子对应的左右边界
int n = heights.length;
int[] lefts = new int[n];
int[] rights = new int[n];
//定义一个栈
Stack<Integer> stack = new Stack<>();
//遍历所有柱子,作为当前高度,先找左边界
for (int i = 0; i < n; i++) {
while (!stack.isEmpty()&&heights[stack.peek()]>=heights[i]){
stack.pop();
}
//所有大于等于当前高度的元素全部弹出,找到了左边界
lefts[i] = stack.isEmpty()?-1:stack.peek();
stack.push(i);
}
stack.clear();
//遍历所有柱子,作为当前高度,先找右边边界
for (int i = n-1; i>=0; i--) {
while (!stack.isEmpty()&&heights[stack.peek()]>=heights[i]){
stack.pop();
}
//所有大于等于当前高度的元素全部弹出,找到了左边界
rights[i] = stack.isEmpty()?n:stack.peek();
stack.push(i);
}
//遍历所有柱子计算面积
for (int i = 0; i <n ; i++) {
int currArea = (rights[i]-lefts[i]-1)*heights[i];
largestArea = currArea>largestArea?currArea:largestArea;
}
return largestArea;
}
//方法五:单调栈优化
public int largestRectangleArea(int[] heights) {
//定义变量保存最大面积
int largestArea= 0;
//定义两个数组,保存每个柱子对应的左右边界
int n = heights.length;
int[] lefts = new int[n];
int[] rights = new int[n];
//初始化rights为右哨兵n
for (int i = 0; i <n ; i++) {
rights[i] = n;
}
//定义一个栈
Stack<Integer> stack = new Stack<>();
//遍历所有柱子,作为当前高度,先找左边界
for (int i = 0; i < n; i++) {
while (!stack.isEmpty()&&heights[stack.peek()]>=heights[i]){
//当前栈顶元素如果小于当前元素,那么它的右边界就是当前元素
rights[stack.peek()]=i;
stack.pop();
}
//所有大于等于当前高度的元素全部弹出,找到了左边界
lefts[i] = stack.isEmpty()?-1:stack.peek();
stack.push(i);
}
//遍历所有柱子计算面积
for (int i = 0; i <n ; i++) {
int currArea = (rights[i]-lefts[i]-1)*heights[i];
largestArea = currArea>largestArea?currArea:largestArea;
}
return largestArea;
}
public static void main(String[] args) {
LargestRectangleInHistorgram largestRectangleInHistorgram = new LargestRectangleInHistorgram();
int[] heights = {2,1,5,6,2,3};
System.out.println(largestRectangleInHistorgram.largestRectangleArea(heights));
}
}