題目連接 : https://leetcode-cn.com/problems/largest-rectangle-in-histogram/java
給定 n 個非負整數,用來表示柱狀圖中各個柱子的高度。每一個柱子彼此相鄰,且寬度爲 1 。python
求在該柱狀圖中,可以勾勒出來的矩形的最大面積。app
以上是柱狀圖的示例,其中每一個柱子的寬度爲 1,給定的高度爲 [2,1,5,6,2,3]。優化
圖中陰影部分爲所能勾勒出的最大矩形面積,其面積爲 10 個單位。spa
輸入: [2,1,5,6,2,3] 輸出: 10
首先,想找到第i
位置最大面積是什麼?code
是以i
爲中心,向左找第一個小於heights[i]
的位置left_i
;向右找第一個小於於heights[i]
的位置right_i
,即最大面積爲heights[i] * (right_i - left_i -1)
,以下圖所示:blog
因此,咱們的問題就變成如何找right_i
和left_i
?leetcode
最簡單的思路就是,就是暴力法,直接分別在i
左右移動get
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: res = 0 n = len(heights) for i in range(n): left_i = i right_i = i while left_i >= 0 and heights[left_i] >= heights[i]: left_i -= 1 while right_i < n and heights[right_i] >= heights[i]: right_i += 1 res = max(res, (right_i - left_i - 1) * heights[i]) return res
可是,這是一個時間複雜度爲$O(n^2)$,超時io
接下來想辦法優化.
思路一:
當咱們找i
左邊第一個小於heights[i]
若是heights[i-1] >= heights[i]
其實就是和heights[i-1]
左邊第一個小於heights[i-1]
同樣.依次類推,右邊同理.
思路二:棧
利用單調棧,我寫過關於它一篇文章
維護一個單調遞增的棧,就能夠找到left_i
和right_i
思路一:
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: if not heights: return 0 n = len(heights) left_i = [0] * n right_i = [0] * n left_i[0] = -1 right_i[-1] = n for i in range(1, n): tmp = i - 1 while tmp >= 0 and heights[tmp] >= heights[i]: tmp = left_i[tmp] left_i[i] = tmp for i in range(n - 2, -1, -1): tmp = i + 1 while tmp < n and heights[tmp] >= heights[i]: tmp = right_i[tmp] right_i[i] = tmp # print(left_i) # print(right_i) res = 0 for i in range(n): res = max(res, (right_i[i] - left_i[i] - 1) * heights[i]) return res
java
class Solution { public int largestRectangleArea(int[] heights) { if (heights == null || heights.length == 0) return 0; int n = heights.length; int[] left_i = new int[n]; int[] right_i = new int[n]; left_i[0] = -1; right_i[n - 1] = n; int res = 0; for (int i = 1; i < n; i++) { int tmp = i - 1; while (tmp >= 0 && heights[tmp] >= heights[i]) tmp = left_i[tmp]; left_i[i] = tmp; } for (int i = n - 2; i >= 0; i--) { int tmp = i + 1; while (tmp < n && heights[tmp] >= heights[i]) tmp = right_i[tmp]; right_i[i] = tmp; } for (int i = 0; i < n; i++) res = Math.max(res, (right_i[i] - left_i[i] - 1) * heights[i]); return res; } }
思路二:
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: stack = [] heights = [0] + heights + [0] res = 0 for i in range(len(heights)): #print(stack) while stack and heights[stack[-1]] > heights[i]: tmp = stack.pop() res = max(res, (i - stack[-1] - 1) * heights[tmp]) stack.append(i) return res
java
class Solution { public int largestRectangleArea(int[] heights) { int res = 0; Deque<Integer> stack = new ArrayDeque<>(); int[] new_heights = new int[heights.length + 2]; for (int i = 1; i < heights.length + 1; i++) new_heights[i] = heights[i - 1]; //System.out.println(Arrays.toString(new_heights)); for (int i = 0; i < new_heights.length; i++) { //System.out.println(stack.toString()); while (!stack.isEmpty() && new_heights[stack.peek()] > new_heights[i]) { int cur = stack.pop(); res = Math.max(res, (i - stack.peek() - 1) * new_heights[cur]); } stack.push(i); } return res; } }