专栏:50多种数据结构彻底征服

专栏:50多种经典图论算法全部掌握

最近在脉脉上看到一张图片,列举了互联网大厂薪资的分布情况,整体来看,多数企业员工薪资集中在 20 - 50K 的区间。其中贝壳在 30 - 50K 薪资段占比最高,为 72.1% ;区间50K以上的,字节和拼多多占比最多,都是5.4%,不过我觉得这种统计比较保守了,应该没有算上加班工资,否则只会更高。

--------------下面是今天的算法题--------------

来看下今天的算法题,这题是LeetCode的第42题:接雨水。

问题描述

来源:LeetCode第42题

难度:困难

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例2:

输入:height = [4,2,0,3,2,5] 输出:9

  • n == height.length

  • 1 <= n <= 2 * 1^04

  • 0 <= height[i] <= 10^5

问题分析

关于接雨水问题我们前面都讲过,有兴趣的可以先看下:

这里再来讲一遍,不过今天的解决方式和之前的不一样,今天使用的是 单调栈 来解决。

如果要计算某个位置是否能接雨水,需要找到这个位置左边和右边有没有比他高的柱子,如果有,那么该位置肯定是能接的。所以我们可以使用一个单调栈,从 栈顶到栈底是单调递增的 。

遍历整个数组,如果数组中的某个元素比栈顶元素m大,说明栈顶元素m遇到了右边比他大的值,这个时候栈顶元素m出栈,出栈之后如果栈不为空,那么新的栈顶元素就是左边比他大的值,既然找到左边和右边都比他大的值,就可以计算位置m所能容纳的水了。

注意这里计算某个位置所容纳的水量不是最终的水量,如下图中,c 位置最终容纳的水是 2 ,但我们计算的时候 c 的左边和右边比他大的分别是 b 和 d ,他们的最小高度是 1 ,所以容纳的水也是 1 。

当计算 d 的时候,左右两边比他大(相等也可以)的是 b 和 e ,因为 d 和 b 的高度一样,所以计算容纳水量为 0 。计算 b 的时候,他的左右两边比他大的分别是 a 和 e ,宽度是 3 ,高度是 1 ,容纳的水量是 3 。

JAVA:

public int trap(int[] height) {     int water = 0;// 容纳的水量     // 单调栈,存放的是柱子的下标,从栈顶到栈底是递增的。     Stack
         
         
  stk =  new Stack<>();      for ( int i =  0; i < height.length; i++) {          // 如果栈不为空,并且当前元素比栈顶元素大          while (!stk.isEmpty() && height[i] > height[stk.peek()]) {              int index = stk.pop(); // 栈顶元素出栈。              // 因为栈从顶到底是递增的,此时如果栈不为空,说明在数组中index左边还有比他高的柱子。              if (!stk.isEmpty()) {                  int left = stk.peek(); // 左边界                  int w = i - left -  1; // 宽度                  int h = Math.min(height[left], height[i]) - height[index];                 water += w * h; // 存数量累加             }         }         stk.push(i);     }      return water; }

C++:

public:     int trap(vector

  &height) {         int water = 0;// 容纳的水量         // 单调栈,存放的是柱子的下标,从栈顶到栈底是递增的。         stack

  stk;         for (int i = 0; i < height.size(); i++) {             // 如果栈不为空,并且当前元素比栈顶元素大             while (!stk.empty() && height[i] > height[stk.top()]) {                 int index = stk.top();                 stk.pop();// 栈顶元素出栈。                 // 因为栈从顶到底是递增的,此时如果栈不为空,说明在数组中index左边还有比他高的柱子。                 if (!stk.empty()) {                     int left = stk.top();// 左边界                     int w = i - left - 1;// 宽度                     int h = min(height[left], height[i]) - height[index];                     water += w * h;// 存数量累加                 }             }             stk.push(i);         }         return water;     }

C:

#define min(a, b) (((a) < (b)) ? (a) : (b)) int trap(int *height, int heightSize) {     int water = 0;// 容纳的水量     // 单调栈,存放的是柱子的下标,从栈顶到栈底是递增的。     int *stk = malloc(20000 * sizeof(int));     int top = 0;// 栈顶     for (int i = 0; i < heightSize; i++) {         // 如果栈不为空,并且当前元素比栈顶元素大         while (top != 0 && height[i] > height[stk[top - 1]]) {             int index = stk[--top];// 栈顶元素出栈。             // 因为栈从顶到底是递增的,此时如果栈不为空,说明在数组中index左边还有比他高的柱子。             if (top != 0) {                 int left = stk[top - 1];// 左边界                 int w = i - left - 1;// 宽度                 int h = min(height[left], height[i]) - height[index];                 water += w * h;// 存数量累加             }         }         stk[top++] = i;     }     return water; }

Python:

def trap(self, height: List[int]) -> int:     water = 0  # 容纳的水量     # 单调栈,存放的是柱子的下标,从栈顶到栈底是递增的。     stk = []     for i, num in enumerate(height):         # 如果栈不为空,并且当前元素比栈顶元素大         while stk and num > height[stk[-1]]:             index = stk.pop()  # 栈顶元素出栈。             # 因为栈从顶到底是递增的,此时如果栈不为空,说明在数组中index左边还有比他高的柱子。             if stk:                 left = stk[-1]  # 左边界                 w = i - left - 1  # 宽度                 h = min(height[left], num) - height[index]                 water += w * h  # 存数量累加         stk.append(i)     return water

笔者简介

博哥,真名:王一博,毕业十多年, 作者,专注于 数据结构和算法 的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解800多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以 下载我整理的1000多页的PDF算法文档 。