这是leetcode面试刷题一题多解系列的第8篇,重点熟悉单调栈算法。
题目
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积
来源:力扣(LeetCode)
链接:leetcode.cn/problems/la…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解1---暴力法
暴力法的思路很简单,我们可以枚举每一个可能的矩形,然后计算出它的面积,并取最大值作为答案。具体来说,我们可以枚举每一个矩形的左右边界,然后找到这个矩形中高度最小的柱子,用它的高度乘上矩形的宽度,就是这个矩形的面积。最后取所有矩形的最大面积作为答案即可。
function largestRectangleArea(heights) {
let n = heights.length;
let ans = 0;
// 枚举所有可能的矩形
for (let i = 0; i < n; i++) {
for (let j = i; j < n; j++) {
// 找到区间 [i, j] 中的最小高度
let h = Math.min(...heights.slice(i, j + 1));
let w = j - i + 1; // 计算矩形的宽度
ans = Math.max(ans, h * w); // 更新最大面积
}
}
return ans;
}
在这个算法中,时间复杂度是O(n^3), 我们需要枚举所有可能的矩形。。 该算法的空间复杂度为 O(1),因为我们只需要常数级别的额外空间来存储矩形的高度和宽度。
题解2---单调栈
单调栈算法是一种利用栈来维护单调性的算法。在某些问题中,我们需要维护一些元素的单调性,例如单调递增或单调递减。这时候可以使用单调栈来解决这类问题。在单调递增的栈中,栈顶元素为当前栈中最大的元素,栈底元素为当前栈中最小的元素。在单调递减的栈中,栈顶元素为当前栈中最小的元素,栈底元素为当前栈中最大的元素。我们可以根据问题的需求选择使用单调递增的栈或单调递减的栈。
function largestRectangleArea(heights) {
let n = heights.length;
let left = new Array(n).fill(0); // 存储每个柱子左侧第一个小于它的柱子的位置
let right = new Array(n).fill(n); // 存储每个柱子右侧第一个小于它的柱子的位置
let stack = []; // 存储单调递增的柱子序号
// 找到每个柱子左侧第一个小于它的柱子的位置
for (let i = 0; i < n; i++) {
// 如果当前柱子高度小于等于栈顶柱子高度,则栈顶柱子右侧第一个小于它的柱子位置为当前柱子位置
while (stack.length > 0 && heights[stack[stack.length - 1]] >= heights[i]) {
right[stack[stack.length - 1]] = i;
stack.pop();
}
left[i] = stack.length == 0 ? -1 : stack[stack.length - 1]; // 当前柱子左侧第一个小于它的柱子位置为栈顶柱子位置
stack.push(i);
}
let ans = 0;
// 枚举每个柱子作为高度的矩形的最大面积
for (let i = 0; i < n; i++) {
ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]); // 更新最大面积
}
return ans;
}
时间复杂度:O(n),当我们从左向右/总右向左遍历数组时,对栈的操作的次数就为 O(n)。所以单调栈的总时间复杂度为O(n)。 空间复杂度:O(n),因为我们需要用两个长度为n的数组分别存储每个柱子左侧第一个小于它的柱子的位置和右侧第一个小于它的柱子的位置。
调栈算法中,我们可以使用栈来存储一些元素,并保持这些元素的单调性。具体来说,我们从左到右遍历数组中的元素,对于每个元素,我们将其加入栈中,如果发现栈顶元素不满足单调性,我们就弹出栈顶元素,直到栈顶元素满足单调性为止。在这个过程中,我们可以利用弹出的元素来解决一些问题。例如,在柱状图中,我们可以使用单调栈来找到每个柱子左侧第一个小于它的柱子和右侧第一个小于它的柱子。
我的更多前端资讯
欢迎大家技术交流 资料分享 摸鱼 求助皆可 —链接