2021年3月13日土曜日天気は良い[過去を嘆いたり、現在を無駄にしたり、未来を恐れたりしないでください]
この記事の内容
1.はじめに
2.さまざまな解決策(数学が最も独創的です)
詳細については、https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-faを参照してください。 -by- w-8 /
2.1列ごとにシーク(ステップバイステップ)
2.1.1暴力法(ダブルループ)
ブルートフォース法:配列をトラバースし、毎回ループを使用して左側と右側の境界値を計算し、この列の水量を取得します。
暴力法は非常に時間的に複雑O(n*n)
ですが、暴力法を理解した後、より良い方法を得るためにそれを改善することができます。
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暴力法の最適化1動的計画法
強引な方法で左側と右側の境界値を計算する場合、計算が大量に繰り返されるため、動的計画法を使用して中間結果を保存し、時間計算量をに減らすことができます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暴力的なメソッドの最適化2-ダブルポインタ、巧妙なソリューション
ダブルポインター法も暴力法の改良版です。一方を強調表示します左右横跳
。左側と右側のどちらか高い方が反対側を横断し、横断する柱の貯水量を計算します。このような横断の後、結果が出て、一定のスペースだけが必要です。
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行ごとにシーク(単調スタック)
単調なスタックは線で解かれ、アイデアを理解するのはより困難です。一般的な原理は次のとおりです。
-
現在の高さがスタックの最上部の高さ以下の場合、スタックが押され、ポインターが後方に移動します。
-
現在の高さがスタックトップの高さよりも大きい場合は、スタックをポップし、現在の壁の高さが以下になるまで、現在のスタックトップで囲まれた領域の列の貯水容量と現在の高さを計算しますスタックの上部の高さまたはスタックが空の場合は、現在の壁をスタックに配置し、ポインタを戻します。
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数学的方法(ベン図、絶対に素晴らしい)
この方法は、leetcode問題の解決策で誤って見られました:https://leetcode-cn.com/problems/trapping-rain-water/solution/wei-en-tu-jie-fa-zui-jian-dan- yi-dong -10xing-jie-j /、ベン図を使って解くと、脳は大きく開いており、一定の空間の複雑さだけが必要であると言えます。
s_right:
右端が閉じていると仮定すると、受け取る
s_left:
ことができる雨の量は閉じられ、受け取ることができる雨の量は
s_rect:
両側で閉じられ、受け取ることができる雨の量は長方形全体。
s_zhu :
列領域
上の図の影付きの領域から見ることができます。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;
}
};
参照
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/