返回

JavaScript剖析 LeetCode 84:柱状图中最大的矩形

前端

探索柱状图中的最大矩形

导读

想像一座座鳞次栉比的大厦排列成壮观的柱状图,每一栋大厦的高度由一个非负整数表示。在这片城市森林中,你打算建造一座面积尽可能大的矩形建筑。那么,你将如何规划它的蓝图?

本文将深入探究这一引人入胜的编程难题,即在柱状图中寻找最大矩形面积。我们将从直观的暴力法入手,逐步揭秘更优雅高效的双指针算法,让你领略算法之美。

暴力出击:双循环法

首先,我们祭出最直白的暴力法:双循环遍历。这种方法简单易懂,但代价是时间复杂度较高。算法的精髓在于,对于每一列,我们以该列的高度作为起点,逐一扩展矩形的宽度,计算所有可能矩形的面积,并从中选出最大的一个。

代码示例:

function largestRectangleArea1(heights) {
  let maxArea = 0;

  for (let i = 0; i < heights.length; i++) {
    for (let j = i; j < heights.length; j++) {
      const currentArea = heights[i] * (j - i + 1);
      maxArea = Math.max(maxArea, currentArea);
    }
  }

  return maxArea;
}

双指针法:优雅的舞步

接下来,我们将介绍双指针法,它在时间复杂度上更胜一筹。算法的巧妙之处在于使用两个指针分别从左右向中心收缩。我们首先设置左右指针在柱状图的两端,然后不断向内移动指针,直到它们相遇。

如何移动指针?

  1. 如果左指针指向的高度比右指针低,则我们移动左指针,因为此时向左扩展可以增加矩形高度。
  2. 如果右指针指向的高度比左指针低,则我们移动右指针,因为向右扩展可以增加矩形宽度。
  3. 在移动指针的同时,我们计算当前高度对应的矩形面积,并将其与最大面积比较,保留更大的一个。

代码示例:

function largestRectangleArea2(heights) {
  let maxArea = 0;
  let left = 0;
  let right = heights.length - 1;

  while (left <= right) {
    const currentArea = Math.min(heights[left], heights[right]) * (right - left + 1);
    maxArea = Math.max(maxArea, currentArea);

    if (heights[left] < heights[right]) {
      left++;
    } else {
      right--;
    }
  }

  return maxArea;
}

效率对比

双循环法的复杂度为 O(n^2),其中 n 是柱状图的长度。双指针法的复杂度则为 O(n),大大降低了时间消耗。

进阶技巧

为了进一步优化算法,我们可以引入一些额外的技巧:

  • 优化暴力法: 预先计算每个高度的最大宽度,从而减少后续枚举的范围。
  • 单调栈: 使用栈来存储已经访问过的高度,避免重复计算。

常见问题解答

  1. 为什么双指针法比暴力法更快?
    因为双指针法只遍历柱状图一次,而暴力法需要遍历柱状图中的每个矩形。
  2. 双指针法可以处理负数高度吗?
    不可以,因为算法假设柱状图中所有高度都是非负数。
  3. 算法是否可以处理有重复高度的柱状图?
    是的,算法可以处理重复高度的情况。
  4. 算法是否可以在线处理输入?
    是的,双指针法可以在线处理输入,即在接收新高度时逐步更新最大面积。
  5. 算法是否可以推广到三维空间?
    是的,可以将算法推广到三维空间中,但需要使用更复杂的数据结构和计算方法。

总结

寻找柱状图中的最大矩形面积是一个经典的算法问题,它考验着我们的算法思维和优化技巧。本文从直观的暴力法到优雅的双指针法,深入探讨了多种解决方法,带领读者领略算法之美。在不断钻研和优化算法的过程中,我们不仅提高了编程技能,更培养了缜密的思维和对解决问题的热情。