Sábado 13 de marzo de 2021, hace buen tiempo [No lamentes el pasado, no desperdicies el presente, no temas el futuro]
Contenido de este artículo
1. Introducción
2. Una variedad de soluciones (las matemáticas son las más ingeniosas)
Para obtener más información, consulte: https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa -por- w-8 /
2.1 Buscar por columna (paso a paso)
2.1.1 Ley violenta (doble bucle)
Método de fuerza bruta: atraviese la matriz, calcule el valor límite de los lados izquierdo y derecho con un bucle cada vez y luego obtenga el volumen de agua en esta columna.
Aunque la ley de violencia es muy compleja en el tiempo O(n*n)
, después de comprender la ley de violencia, se puede mejorar para obtener un método mejor.
class Solution {
public:
int trap(vector<int>& height) {
const int n = height.size();
int res = 0;
for(int i=1;i<n-1;++i){
int max_left = 0, max_right = 0;
for(int j=i;j>=0;--j)
max_left = max(max_left,height[j]);
for(int j=i;j<n;++j)
max_right = max(max_right,height[j]);
res += min(max_left,max_right)-height[i];
}
return res;
}
};
2.1.2 Programación unidinámica de optimización de la ley de violencia
Cuando el método de fuerza bruta calcula los valores límite de los lados izquierdo y derecho, hay una gran cantidad de cálculos repetidos, por lo que la programación dinámica se puede utilizar para almacenar resultados intermedios y reducir la complejidad del tiempo a O(n)
.
class Solution {
public:
int trap(vector<int>& height) {
if(height.empty()) return 0;
const int n = height.size();
int ans = 0;
vector<int> max_left(n), max_right(n);
max_left[0] = height[0];
max_right[n-1] = height[n-1];
for(int i=1;i<n;++i)
max_left[i] = max(max_left[i-1],height[i]);
for(int i=n-2;i>=0;--i)
max_right[i] = max(max_right[i+1],height[i]);
for(int i=0;i<n;++i)
ans += min(max_left[i],max_right[i])-height[i];
return ans;
}
};
2.1.3 Optimización de métodos violentos de dos punteros dobles, solución inteligente
El método de doble puntero también es una mejora del método violento. Destaca uno 左右横跳
. El lado que esté más alto en los lados izquierdo y derecho atravesará el otro lado y calculará el almacenamiento de agua de la columna que se está atravesando. Después de dicho recorrido, saldrá el resultado, y solo se necesita un espacio constante.
class Solution {
public:
int trap(vector<int>& height) {
if(height.empty()) return 0;
int i = 0, j = height.size()-1;
int left_max = height[i], right_max = height[j];
int ans = 0;
while(i<j){
if(height[i]<=height[j]){
++i;
left_max = max(left_max,height[i]);
if(height[i]<left_max)
ans += left_max-height[i];
}
else {
--j;
right_max = max(right_max,height[j]);
if(height[j]<right_max)
ans += right_max-height[j];
}
}
return ans;
}
};
2.2 Buscar por línea (pila monótona)
La pila monótona se resuelve por línea y la idea es más difícil de entender. El principio general es:
-
Cuando la altura actual es menor o igual que la altura de la parte superior de la pila, la pila se empuja y el puntero se mueve hacia atrás.
-
Cuando la altura actual es mayor que la altura de la parte superior de la pila, saque la pila y luego calcule la capacidad de almacenamiento de agua de la fila del área encerrada por la parte superior de la pila actual y la altura actual hasta que la altura de la pared actual sea menor o igual a la altura superior de la pila o la pila vacía, y luego coloque la pared actual en la pila, puntero Mover hacia atrás.
class Solution {
public:
int trap(vector<int>& height) {
const int n = height.size();
stack<int> st;
int res = 0, cur = 0;
while(cur<n){
while(!st.empty() && height[st.top()]<height[cur]){
int top = height[st.top()];
st.pop();
if(st.empty()) break;
int d = cur-st.top()-1;
int h = min(height[cur],height[st.top()])-top;
res += d*h;
}
st.push(cur++);
}
return res;
}
};
2.3 Método matemático (diagrama de Venn, absolutamente impresionante)
Este método se vio accidentalmente en la solución del problema de leetcode: https://leetcode-cn.com/problems/trapping-rain-water/solution/wei-en-tu-jie-fa-zui-jian-dan- yi-dong -10xing-jie-j / , usando el diagrama de Venn para resolver, se puede decir que el cerebro está completamente abierto y solo se requiere una complejidad espacial constante.
s_right:
Suponiendo que el extremo derecho está cerrado, la cantidad de lluvia
s_left:
que se puede recibir se cierra y la cantidad de lluvia que se puede recibir
s_rect:
se cierra en ambos lados, y la cantidad de lluvia que se puede recibir es el área del rectángulo completo.
s_zhu :
Área de la columna
Se puede ver en el área sombreada de la figura anterior:s_left + s_right - s_rect = s_zhu + res
class Solution {
public:
int trap(vector<int>& height) {
if(height.empty()) return 0;
const int n = height.size();
int left_max = height[0], right_max = height[n-1];
// s_left:假设最左边是封闭的,能接到的雨水量
// s_right:假设最右边是封闭的,能接到的雨水量
// s_rect:假设两边都是封闭的,能接到的雨水量,也就是矩形的面积
// s_left + s_right - s_rect = s_zhu + res
int s_left = 0, s_right = 0, s_zhu = 0, h_max = height[0];
for(int i=0;i<height.size();++i){
left_max = max(left_max,height[i]);
s_left += left_max;
right_max = max(right_max,height[n-1-i]);
s_right += right_max;
h_max = max(h_max,height[i]);
s_zhu += height[i];
}
int s_rect = h_max*n;
return s_left + s_right - s_zhu - s_rect;
}
};
referencias
https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
https://leetcode-cn.com/problems/trapping-rain-water/solution/wei-en-tu-jie-fa-zui-jian-dan-yi-dong-10xing-jie-j/