接雨水的题目描述如下。
(1) 2D接雨水: 字节员工是不是个个都会接雨水 ;
(2) 3D接雨水: 407. 接雨水 II ;
(3) 3D接雨水: 字节人都会的 3D接雨水 。
问题描述
难度:困难
给你一个 m x n 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。
示例1:
输入:heightMap = [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]];
输出:4
解释:下雨后,雨水将会上图蓝色的方块中。总的接雨水量为1+1+1=4。
我找到的最好的思路是:把这个看成木桶接水,就像是短板理论。最外层一圈一定接不到水,也就是可以看成木桶的边缘,先把木桶的边缘都放进优先队列中,这时取出最小的柱子,向这个柱子的 4 个方向扩散,扩散出的柱子有两种情况,如果比当前柱子大,那么不能接水,如果比当前柱子小,那么接到的水就是当前柱子高度减去扩散柱子的高度(为什么?因为当前柱子一定是边缘最小的了,所以它一定能接到水),后面在把扩散的柱子加入到优先队列中,已经比较完的柱子就移出队列,这样就又形成了新的桶的边缘,并且水桶面积也在不断缩小(当然要记录走过的柱子,防止重复走),最终就会计算完成。
下面是用C语言实现的3D接雨水:
#include <stdio.h>
#include <stdlib.h>#define Cols (6)
#define Rows (3)#define Status_ToDo (0) // 待处理的。
#define Status_Done (1) // 已处理的。
#define Status_Border (2) // 作为木桶板。int addRainWaterOfTBLR(int height[][Cols], int status[][Cols], int minBorderHeight, int row, int col);void initStatus(int arr[][Cols]) {// // 注: 使用下面语句得到的rowCount为0, 因为arr作为形参的时候就会被当作一个指针。// int rowCount = sizeof(arr) / sizeof(arr[0]);for (int row = 0; row < Rows; row++) {for (int col = 0; col < Cols; col++) {if (0 == row || Rows-1 == row || 0 == col || Cols-1 == col) {arr[row][col] = Status_Border;} else {arr[row][col] = Status_ToDo;}}}// 去掉四个角。arr[0][0] = arr[0][Cols-1] = arr[Rows-1][0] = arr[Rows-1][Cols-1] = Status_Done;
}void showStatus(int height[][Cols], int status[][Cols]) {printf("打印状态:\n");for (int row = 0; row < Rows; row++) {for (int col = 0; col < Cols; col++) {printf("%d(%d), ", height[row][col], status[row][col]);}printf("\n");}
}void findPositionOfMinBorder(int height[][Cols], int status[][Cols], int result[2]) {// printf("findPositionOfMinBorder\n");result[0] = 0;result[1] = 0;for (int row = 0; row < Rows; row++) {for (int col = 0; col < Cols; col++) {if (Status_Border == status[row][col]&& ((0 == result[0] && 0 == result[1]) || height[row][col] < height[result[0]][result[1]])) {result[0] = row;result[1] = col;}}}printf("最小板的位置是(%d,%d)\n", result[0], result[1]);
}void repairBorder(int status[][Cols]) {for (int row = 0; row < Rows; row++) {for (int col = 0; col < Cols; col++) {if (Status_Border == status[row][col]) {//上下左右都不是待处理的。if ((0 == row || (row > 0 && status[row-1][col] != Status_ToDo))&& (Rows-1 == row || (Rows-1 > row && status[row+1][col] != Status_ToDo))&& (0 == col || (col > 0 && status[row][col-1] != Status_ToDo))&& (Cols-1 == col || (Cols-1 > col && status[row][col+1] != Status_ToDo))) {status[row][col] = Status_Done;}}}}
}int addRainWaterAtPosition(int height[][Cols], int status[][Cols], int minBorderHeight, int row, int col) {int result = 0;// printf("addRainWaterAtPosition(%d,%d)\n", row, col);if (height[row][col] <= minBorderHeight) {result += (minBorderHeight - height[row][col]);result += addRainWaterOfTBLR(height, status, minBorderHeight, row, col);} else {status[row][col] = Status_Border;}return result;
}int addRainWaterOfTBLR(int height[][Cols], int status[][Cols], int minBorderHeight, int row, int col) {int result = 0;// printf("addRainWaterOfTBLR(%d,%d)\n", row, col);status[row][col] = Status_Done;if (row > 0 && Status_ToDo == status[row-1][col]) { // 上。result += addRainWaterAtPosition(height, status, minBorderHeight, row-1, col);}if (row < Rows-1 && Status_ToDo == status[row+1][col]) { // 下。result += addRainWaterAtPosition(height, status, minBorderHeight, row+1, col);}if (col > 0 && Status_ToDo == status[row][col-1]) { // 左。result += addRainWaterAtPosition(height, status, minBorderHeight, row, col-1);}if (col < Cols-1 && Status_ToDo == status[row][col+1]) { // 右。result += addRainWaterAtPosition(height, status, minBorderHeight, row, col+1);}return result;
}// 判断完成。
int checkFinish(int status[][Cols]) {for (int row = 0; row < Rows; row++) {for (int col = 0; col < Cols; col++) {if (Status_ToDo == status[row][col]) {return 0;}}}return 1;
}// 简介: 3D接雨水。
int main(void)
{int rainWater = 0;int heightMap[][Cols] = {{1,4,3,1,3,2},{3,2,1,3,2,4},{2,3,3,2,3,1}};// printf("heightMap_rowCount = %d。\n", sizeof(heightMap) / sizeof(heightMap[0]));int (*status)[Cols] = malloc(sizeof(int) * (sizeof(heightMap) / sizeof(heightMap[0])) * Cols);initStatus(status);showStatus(heightMap, status);while(!checkFinish(status)) {int positionOfMinBorder[2] = {0, 0};findPositionOfMinBorder(heightMap, status, positionOfMinBorder);int minBorderHeight = heightMap[positionOfMinBorder[0]][positionOfMinBorder[1]];// printf("minBorderHeight=%d\n", minBorderHeight);rainWater += addRainWaterOfTBLR(heightMap, status, minBorderHeight, positionOfMinBorder[0], positionOfMinBorder[1]);repairBorder(status);showStatus(heightMap, status);}printf("总的接雨水量为%d\n", rainWater);free(status);printf("程序正常退出。\n");return 0;
}