Code Caprice Training Camp day60| 84. The largest rectangle in the histogram

@TOC


foreword

Code Random Record Algorithm Training Camp day60


1. Leetcode 84. The largest rectangle in the histogram

1. Topic

Given n non-negative integers, used to represent the height of each column in the histogram. Each column is adjacent to each other and has a width of 1.

Find the maximum area of ​​the rectangle that can be outlined in the histogram.

Example 1:

Input: heights = [2,1,5,6,2,3] Output: 10 Explanation: The largest rectangle is the red area in the picture, with an area of ​​10

Example 2:

Input: heights = [2,4] Output: 4

hint:

1 <= heights.length <=105
0 <= heights[i] <= 104

Source: LeetCode Link: https://leetcode.cn/problems/largest-rectangle-in-histogram

2. Problem-solving ideas

Method 1: Monotonic stack

train of thought

Let's summarize the method of enumerating "high":

首先我们枚举某一根柱子 ii 作为高 h=heights[i]h=heights[i];

随后我们需要进行向左右两边扩展,使得扩展到的柱子的高度均不小于 hh。换句话说,我们需要找到左右两侧最近的高度小于 hh 的柱子,这样这两根柱子之间(不包括其本身)的所有柱子高度均不小于 hh,并且就是 ii 能够扩展到的最远范围。

So let's first take a look at how to find the nearest column on the left side of a column that is smaller than its height. In addition to enumerating violently according to the "preface", we can think deeply through the following conclusion:

对于两根柱子 j0j0​ 以及 j1j1​,如果 j0<j1j0​<j1​ 并且 heights[j0]≥heights[j1]heights[j0​]≥heights[j1​],那么对于任意的在它们之后出现的柱子 ii(j1<ij1​<i),j0j0​ 一定不会是 ii 左侧且最近的小于其高度的柱子。

In other words, if there are two pillars j0j0​ and j1j1​, where j0j0​ is on the left side of j1j1​, and the height of j0j0​ is greater than or equal to j1j1​, then when the following pillar ii goes to the left to find a pillar less than its height , j1j1​ will "block" j0j0​, j0j0​ will not be the answer.

In this way, we can traverse the array from left to right, while maintaining a "possible answer" data structure, in which some jj values ​​are stored in ascending order. According to the above conclusion, if we store j0,j1,⋯,jsj0​,j1​,⋯,js​, then there must be height[j0]

When we enumerate to the ii-th column, j0,j1,⋯,jsj0​,j1​,⋯,js​ are stored in our data structure, if the left side of the ii-th column and the nearest column smaller than its height is jiji​, then there must be

height[j0]

In this way, we can use the binary search method to find the jiji corresponding to ii​, but is it really necessary? When we enumerate to i+1i+1, the original ii also becomes jj value, so ii will be put into the data structure. Since all jj values ​​in the data structure are less than ii, then all jj whose height is greater than or equal to height[i]height[i] will not be the answer and need to be removed from the data structure. And we found that these removed jj values ​​happen to be

ji+1,⋯ ,jsji+1​,⋯,js​

In this way, when we enumerate to the ii-th column, we can first remove all the jj values ​​whose height is greater than or equal to height[i]height[i], and the one with the highest height among the remaining jj values ​​is the answer. After that, we put ii into the data structure and start the next enumeration. At this point, the data structure we need to use is ready to come out, it is the stack.

栈中存放了 jj 值。从栈底到栈顶,jj 的值严格单调递增,同时对应的高度值也严格单调递增;

当我们枚举到第 ii 根柱子时,我们从栈顶不断地移除 height[j]≥height[i]height[j]≥height[i] 的 jj 值。在移除完毕后,栈顶的 jj 值就一定满足 height[j]<height[i]height[j]<height[i],此时 jj 就是 ii 左侧且最近的小于其高度的柱子。
    这里会有一种特殊情况。如果我们移除了栈中所有的 jj 值,那就说明 ii 左侧所有柱子的高度都大于 height[i]height[i],那么我们可以认为 ii 左侧且最近的小于其高度的柱子在位置 j=−1j=−1,它是一根「虚拟」的、高度无限低的柱子。这样的定义不会对我们的答案产生任何的影响,我们也称这根「虚拟」的柱子为「哨兵」。

我们再将 ii 放入栈顶。

The elements stored in the stack are monotonic, which is the classic data structure "monotonic stack".

example

We use a specific example [6,7,5,2,4,5,9,3][6,7,5,2,4,5,9,3] to help readers understand the monotonic stack. We need to find the left side of each column and the nearest column smaller than its height. Initially the stack is empty.

我们枚举 66,因为栈为空,所以 66 左侧的柱子是「哨兵」,位置为 -1。随后我们将 66 入栈。
    栈:[6(0)]。(这里括号内的数字表示柱子在原数组中的位置)

我们枚举 77,由于 6<76<7,因此不会移除栈顶元素,所以 77 左侧的柱子是 66,位置为 00。随后我们将 77 入栈。
    栈:[6(0), 7(1)]

我们枚举 55,由于 7≥57≥5,因此移除栈顶元素 77。同样地,6≥56≥5,再移除栈顶元素 66。此时栈为空,所以 55 左侧的柱子是「哨兵」,位置为 −1−1。随后我们将 55 入栈。
    栈:[5(2)]

接下来的枚举过程也大同小异。我们枚举 22,移除栈顶元素 55,得到 22 左侧的柱子是「哨兵」,位置为 −1−1。将 22 入栈。
    栈:[2(3)]

我们枚举 44,55 和 99,都不会移除任何栈顶元素,得到它们左侧的柱子分别是 22,44 和 55,位置分别为 33,44 和 55。将它们入栈。
    栈:[2(3), 4(4), 5(5), 9(6)]

我们枚举 33,依次移除栈顶元素 99,55 和 44,得到 33 左侧的柱子是 22,位置为 33。将 33 入栈。
    栈:[2(3), 3(7)]

In this way, we get the column numbers on the left of them as [−1,0,−1,−1,3,4,5,3][−1,0,−1,−1,3,4,5 ,3]. Using the same method, we traverse from right to left, and we can also get the column numbers on the right of them as [2,2,3,8,7,7,7,8][2,2,3,8, 7,7,7,8], here we regard position 88 as a "sentinel".

After getting the columns on the left and right sides, we can calculate the left and right boundaries corresponding to each column, and find the answer.

analyze

What is the time complexity of a monotonic stack? It is very difficult to calculate directly, but we can find that:

每一个位置只会入栈一次(在枚举到它时),并且最多出栈一次。

So when we traverse the array from left to right/total right to left, the number of operations on the stack is O(N)O(N). So the total time complexity of the monotonic stack is O(N)O(N).

3. Code implementation

```java class Solution { public int largestRectangleArea(int[] heights) { int n = heights.length; int[] left = new int[n]; int[] right = new int[n];

Deque<Integer> mono_stack = new ArrayDeque<Integer>();
    for (int i = 0; i < n; ++i) {
        while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
            mono_stack.pop();
        }
        left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
        mono_stack.push(i);
    }

    mono_stack.clear();
    for (int i = n - 1; i >= 0; --i) {
        while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
            mono_stack.pop();
        }
        right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek());
        mono_stack.push(i);
    }

    int ans = 0;
    for (int i = 0; i < n; ++i) {
        ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
    }
    return ans;
}

}

Guess you like

Origin blog.csdn.net/HHX_01/article/details/131285451