1. 题目
2. 解法
有两种常见的方法:
- 一种是使用数组和滑动窗口,
- 另一种是使用单调栈。
数组和滑动窗口
思路
使用数组和滑动窗口的方法的思路是:
- 对于每个元素,如果我们知道它左右两边第一个比它小的元素的下标,那么我们就可以计算出以它为高度的最大矩形的宽度,即右边界减去左边界再减一。
- 为了找到每个元素的左右边界,我们可以使用两个数组left和right来存储它们。对于left数组,我们从左往右遍历原数组,用一个临时变量tmp来记录当前元素的左边界,初始值为i-1。然后我们比较tmp和i位置的元素,如果tmp位置的元素大于等于i位置的元素,说明tmp不是i的左边界,我们需要更新tmp为left[tmp],即tmp位置元素的左边界。这样我们就可以跳过一些不必要的比较,直到找到第一个比i位置元素小的元素或者越界。最后我们把tmp赋值给left[i]。对于right数组,我们从右往左遍历原数组,用类似的方法来更新right[i]。
- 在得到了left和right数组后,我们再遍历一遍原数组,对于每个元素,计算以它为高度的最大矩形面积,并更新最大值。
代码逻辑
使用数组和滑动窗口的方法的代码逻辑是:
- 定义一个函数largestRectangleArea,接受一个整型数组heights作为参数,返回一个整型值作为结果。
- 判断heights的长度是否为0或1,如果是,则直接返回heights[0]或0。
- 定义两个整型数组left和right,分别用来存储每个元素的左右边界,初始化left[0]为-1,right[len-1]为len。
- 定义一个整型变量len,用来存储heights的长度。
- 用一个for循环从1到len-1遍历heights数组,定义一个整型变量tmp,初始化为i-1。用一个while循环判断tmp是否大于等于0且heights[tmp]是否大于等于heights[i],如果是,则更新tmp为left[tmp]。最后把tmp赋值给left[i]。
- 用一个for循环从len-2到0遍历heights数组,定义一个整型变量tmp,初始化为i+1。用一个while循环判断tmp是否小于len且heights[tmp]是否大于等于heights[i],如果是,则更新tmp为right[tmp]。最后把tmp赋值给right[i]。
- 定义一个整型变量maxArea,用来存储最大矩形面积,初始化为0。
- 用一个for循环从0到len-1遍历heights数组,定义一个整型变量width,用来存储以当前元素为高度的最大矩形的宽度,计算方法为right[i]-left[i]-1。定义一个整型变量area,用来存储以当前元素为高度的最大矩形面积,计算方法为width*heights[i]。用maxArea和area比较并更新maxArea。
- 返回maxArea作为结果。
具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public int largestRectangleArea( int [] heights) { int len = heights.length; if (len == 0 ) { return 0 ; } if (len == 1 ) { return heights[ 0 ]; } // 使用数组来模拟栈,避免频繁的出栈入栈操作 int [] left = new int [len]; int [] right = new int [len]; // 初始化 left[ 0 ] = - 1 ; right[len - 1 ] = len; // 遍历数组,计算每个元素左边第一个比它小的元素的下标 for ( int i = 1 ; i < len; i++) { int tmp = i - 1 ; while (tmp >= 0 && heights[tmp] >= heights[i]) { tmp = left[tmp]; } left[i] = tmp; } // 遍历数组,计算每个元素右边第一个比它小的元素的下标 for ( int i = len - 2 ; i >= 0 ; i--) { int tmp = i + 1 ; while (tmp < len && heights[tmp] >= heights[i]) { tmp = right[tmp]; } right[i] = tmp; } // 遍历数组,计算每个元素能构成的最大矩形面积 int maxArea = 0 ; for ( int i = 0 ; i < len; i++) { int width = right[i] - left[i] - 1 ; int area = width * heights[i]; maxArea = Math.max(maxArea, area); } return maxArea; } |
使用单调栈
思路
使用单调栈的方法的思路是:
- 为了找到每个元素的左右边界,我们可以使用一个单调递增的栈来存储元素的下标。这样,栈顶元素就是当前元素的左边界,而当遇到一个比栈顶元素小的元素时,就说明找到了栈顶元素的右边界。
- 我们遍历数组,对于每个元素,我们先判断栈是否为空或者当前元素是否大于等于栈顶元素,如果是,则将当前元素的下标入栈。如果不是,则说明当前元素是栈顶元素的右边界,我们就弹出栈顶元素,并计算以它为高度的最大矩形面积。如果此时栈为空,说明栈顶元素没有左边界,否则栈顶元素就是它的左边界。我们重复这个过程,直到栈为空或者当前元素大于等于栈顶元素。
- 在遍历完数组后,如果栈不为空,说明还有一些元素没有找到右边界,我们可以假设它们的右边界是数组的长度。我们再次弹出栈顶元素,并计算以它为高度的最大矩形面积。如果此时栈为空,说明栈顶元素没有左边界,否则栈顶元素就是它的左边界。我们重复这个过程,直到栈为空。
代码逻辑
使用单调栈的方法的代码逻辑是:
- 定义一个函数largestRectangleArea,接受一个整型数组heights作为参数,返回一个整型值作为结果。
- 判断heights的长度是否为0或1,如果是,则直接返回heights[0]或0。
- 定义一个整型变量len,用来存储heights的长度。
- 定义一个整型变量maxArea,用来存储最大矩形面积,初始化为0。
- 定义一个整型栈stack,用来存储元素的下标,保持栈内元素对应的高度单调递增。
- 用一个for循环从0到len-1遍历heights数组,定义一个整型变量height,用来存储当前元素的高度。
- 用一个while循环判断栈是否为空或者height是否小于栈顶元素对应的高度,如果是,则定义一个整型变量curHeight,用来存储弹出的栈顶元素对应的高度。定义一个整型变量leftBoundary,用来存储弹出的栈顶元素的左边界,如果此时栈为空,则赋值为-1,否则赋值为栈顶元素。定义一个整型变量rightBoundary,用来存储弹出的栈顶元素的右边界,赋值为当前遍历到的下标i。定义一个整型变量width,用来存储以弹出的栈顶元素为高度的最大矩形的宽度,计算方法为rightBoundary-leftBoundary-1。定义一个整型变量area,用来存储以弹出的栈顶元素为高度的最大矩形面积,计算方法为width*curHeight。用maxArea和area比较并更新maxArea。
- 在while循环结束后,将当前遍历到的下标i入栈。
- 在for循环结束后,如果栈不为空,则说明还有一些元素没有找到右边界。我们再次用一个while循环
具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public int largestRectangleArea( int [] heights) { int len = heights.length; if (len == 0 ) { return 0 ; } if (len == 1 ) { return heights[ 0 ]; } // 使用栈来存储元素下标,保持栈内元素对应的高度单调递增 Stack<Integer> stack = new Stack<>(); // 遍历数组,对于每个元素,找到它左边和右边第一个比它小的元素的下标 int maxArea = 0 ; for ( int i = 0 ; i < len; i++) { int height = heights[i]; // 如果栈不为空且当前高度小于栈顶高度,说明找到了右边界 while (!stack.isEmpty() && height < heights[stack.peek()]) { // 弹出栈顶元素,计算以它为高度的最大矩形面积 int curHeight = heights[stack.pop()]; // 如果栈为空,说明左边界不存在,否则左边界就是栈顶元素 int leftBoundary = stack.isEmpty() ? - 1 : stack.peek(); // 右边界就是当前元素 int rightBoundary = i; // 计算宽度和面积 int width = rightBoundary - leftBoundary - 1 ; int area = width * curHeight; maxArea = Math.max(maxArea, area); } // 将当前元素下标入栈 stack.push(i); } // 如果遍历完数组后栈不为空,说明还有一些元素没有找到右边界 while (!stack.isEmpty()) { // 弹出栈顶元素,计算以它为高度的最大矩形面积 int curHeight = heights[stack.pop()]; // 如果栈为空,说明左边界不存在,否则左边界就是栈顶元素 int leftBoundary = stack.isEmpty() ? - 1 : stack.peek(); // 右边界就是数组的长度 int rightBoundary = len; // 计算宽度和面积 int width = rightBoundary - leftBoundary - 1 ; int area = width * curHeight; maxArea = Math.max(maxArea, area); } return maxArea; } |
3. 总结
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2021-04-25 Dubbo源码分析(十)同步调用与异步调用
2021-04-25 Dubbo源码分析(九)负载均衡算法
2021-04-25 Dubbo源码分析(八)集群容错机制
2021-04-25 Dubbo源码分析(七)服务目录
2021-04-25 Dubbo源码分析(六)服务引用的具体流程
2021-04-25 Dubbo源码分析(五)服务暴露的具体流程(下)
2021-04-25 Dubbo源码分析(四)服务暴露的具体流程(上)