攒青豆

45 阅读2分钟

当青训营遇上码上掘金

主题4:攒青豆

小声bb: 嗯--本质上来说,这就是leetcode的接雨水,想要更详细的解答直接去看接雨水吧,这也是历届秋招高频面试题,如果这题都不会秋招就悬了。

题目:攒青豆

给你一个数组,表示n个柱子的高度,每个柱子宽度为1,从上往下撒青豆,最后柱子缝里能接住多少青豆。 比如

vector<int> height = {5, 0, 2, 1, 4, 0, 1, 0, 3}

则从往右柱子缝里最多接

{0, 4, 2, 3, 0, 3, 2, 3, 0}

加起来也就是17颗青豆。

我们来分析一下这个题目,如果讨论每个柱子即横坐标能放多少颗青豆,是取决于这个柱子左边最高的柱子和右边最高的柱子上较矮的那个,然后这个值减去当前横坐标对应高度即可,因此用以上方法最后遍历所有柱子并求和即可。

那么难点就在于如何复杂度更低的求出每个柱子左边和右边最高柱子中较矮的那个,如果对每个横坐标都进行一次数组遍历,那么时间复杂度难免会到O(n^2),但显然这个题最优可到O(n)

以下给出一种O(n)解法

// 攒青豆
int Solution(vector<int>& height) {
  int n = height.size();
  vector<int> leftMost(n, 0);
  vector<int> rightMost(n ,0);
  int curLeftMost = 0;
  int curRightMost = 0;
  for (int i = 0; i < n; i++) {
    if (height[i] > curLeftMost) {
      curLeftMost = height[i];
    }
    leftMost[i] = curLeftMost;
  }
  for (int i = n - 1; i >= 0; i--) {
    if (height[i] > curRightMost) {
      curRightMost = height[i];
    }
    rightMost[i] = curRightMost;
  }
  int res = 0;
  for (int i = 0; i < n; i++) {
    res += min(rightMost[i], leftMost[i]) - height[i];
  }
  return res;
}

主要是建立两个数组,表示每个柱子左侧和右侧最高柱子的高度,然后从左往后以及从右往左扫描统计对应高度,时间复杂度便可以减少到O(n)

当然,上面的方法还可以进一步的空间上的优化,比如可以只用一个数组统计右侧最高柱子的高度,然后从左往右扫描的过程中便一边统计左侧最高柱子的高度一边计算结果即可。这里代码就不给出了。