タイトル説明
https://leetcode-cn.com/problems/trapping-rain-water/
解決
解決策1:AC残業は不可能です(垂直トラバーサル)
下から上に向かって、各レイヤーの累積を計算します。開始と終了が維持されるたびに、レイヤーの数が最大数になります。
class Solution {
public int trap(int[] height) {
//一层一层的计算
//维护一个层数,和计算的雨水
if(height==null ||height.length<3) return 0;
int res = 0;
int max = 0;
for(int i=0;i<height.length;i++){
if(max<height[i]){
max = height[i];
}
}
for(int i=0;i<=max;i++){
//层数,层数是最大值
res +=find(height,i);
}
return res;
}
/*
*返回第i层的积水数
*/
public int find(int[]height,int high){
int start=-1,end=-1;
//必须大于当前的层数的值,这之间的柱子才可能有积水
for(int i=0;i<height.length;i++){
if(height[i]>high){
start = i;
break;
}
}
for(int j=height.length-1;j>0;j--){
if(height[j]>high){
//0我就要找比他大的
end = j;
break;
}
}
if((start!=-1 && end!=-1) && start<end){
//满足条件则有积水
int res = 0;
for(int i=start;i<end;i++){
if(height[i]<=high){
//小于当前的层数,则
res++;
}
}
return res;
}
return 0;
}
}
解決策2:シングルグリッド法(水平トラバーサル)
上記のアルゴリズムは垂直方向の寸法から考えています。次に、水平方向の寸法を見て、各グリッドに格納できる水の高さを計算し、一度に1つのグリッドの水の単位のみを計算する必要があります。
class Solution {
public int trap(int[] height) {
//一个格子一个格子的计算
if(height==null ||height.length<3) return 0;
int res = 0;
for(int i=1;i<height.length-1;i++){
//从1开始才有积水
int maxLeft = 0,maxRight = 0;
for(int j=i-1;j>=0;j--){
maxLeft = Math.max(maxLeft,height[j]);
}
for(int j=i+1;j<height.length;j++){
maxRight = Math.max(maxRight,height[j]);
}
int min = Math.min(maxRight,maxLeft);
if(min - height[i]<=0)//每个格子能积多少水
continue;//不能积水
else res += min-height[i];
}
return res;
}
}
時間計算量はO(n * n)に達し、空間計算量はO(1)です。左側と右側を見つけるのに時間がかかることに注意してください。高さをまったく更新する必要がない場合もあるので、次のことができます。これらの値を事前に保存してください
解決策3:動的計画
法rightMax配列を使用してiの最大高さを右に格納し、leftMax配列を使用してiの最大高さを左に格納します。
class Solution {
public int trap(int[] height) {
//一个格子一个格子的计算其上的积水单位
if(height==null ||height.length<3) return 0;
int res = 0;
int[]leftMax = new int[height.length];
int[]rightMax = new int[height.length];
leftMax[0] = height[0];
rightMax[height.length-1] = height[height.length-1];
//使用一个rightMax来存储i往右的最大高度柱子 使用一个leftMax来存储i往左边的最大高度柱子
for(int i=1,j=height.length-2;i<height.length && j>=0;i++,j--){
leftMax[i] = Math.max(height[i],leftMax[i-1]);
rightMax[j] = Math.max(height[j],rightMax[j+1]);
}
for(int i=1;i<height.length;i++){
int min = Math.min(leftMax[i],rightMax[i]);
if(min - height[i]<=0)//每个格子能积多少水
continue;//不能积水
else res += min-height[i];
}
return res;
}
}
解決策4:ダブルポインターメソッドダブルポインター
を使用します。メソッド2では、どの値がトラバース方向を決定しますか。
- たとえば、右側の値が大きい場合、左側の列の水蓄積単位を計算できます。
左側のものが小さいので、左側に水単位がある可能性が高いことがわかり
ます。右側のポインターは移動せず、左側のポインターは移動します。列を計算した後 - 逆に、左のポインタは移動せず、右のポインタは移動します
class Solution {
public int trap(int[] height) {
//一个格子一个格子的计算其上的积水单位
if(height==null ||height.length<3) return 0;
int res = 0;
int left = 0;int right = height.length-1;
int leftMax = height[0],rightMax=height[right];
//双指针
while(left<right){
//判断哪个大
if(height[left]<height[right]){
//我们则计算左边的柱子的积水
if(height[left]>leftMax ){
//没有积水,当前的left柱子上
leftMax=height[left];
}else//有积水
res+=leftMax-height[left];
//左边指针移动一位
left++;
}else{
//我们则计算右边的柱子的积水
if(height[right]>rightMax ){
//没有积水,当前的right柱子上
rightMax=height[right];
}else//有积水
res+=rightMax-height[right];
//右边指针移动一位
right--;
}
}
return res;
}
}