質問の詳細
幅1の各列の高さマップを表すn個の非負の整数が与えられた場合、このように配置された列が受け取ることができる雨水量を計算します。
例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:
入力:高さ= [4,2,0,3,2,5]
出力:9
3つの解決策
最初の解決策は私自身の考えです。最初のより暴力的な方法は、実際にはスタックに少し似ています。
面積はさまざまなレベルで計算されます。
public int trap(int[] height) {
//思路有了,这题和11题一点都不一样,需要考虑的东西实际很复杂
//也是双指针,但需要记录下当前的高度,每次高度来计算一下面积
//当后面的高度大于前面的,只需要记录下当前由于高度增加而新增的面积,从而防止重复计算
int l=0, r=height.length-1, res=0;
int cur_hi = 0;//当前高度
while(l<r){
//只要当高度更新了,才会计算面积
if(Math.min(height[l],height[r])>cur_hi){
res += calcArea(height,cur_hi,l,r);
cur_hi = Math.min(height[l],height[r]);
}
//移动l与r
if(height[l]<=height[r]){
l++;
}
else{
r--;
}
}
return res;
}
/**
计算中间能接多少雨水
**/
private int calcArea(int[] height,int cur_hi, int l, int r){
if(r-l<2){
return 0;
}
int real_height = Math.min(height[l],height[r]);
int area = 0;
for(int i=l+1; i<r; i++){
//每个节点的高度如果低于cur_hi,就按cur_hi来看,如果高于cur_hi,就按自己看
int cur = height[i]>cur_hi ? height[i] : cur_hi;
//每次按照边界和高度确定当前的接水高度
area += Math.max(0,real_height-cur);
}
return area;
}
このソリューションは剪定を最大限に活用しますが、複雑さがO(n2)であるため、少し遅くなります。4ms
次に、ダブルポインタO(n)の解があります。
public int trap(int[] height) {
// 来进行双指针O(n)的练习
// 这里的逻辑是双指针进行走,每次记录左右已遍历最大值
// 如果移动到当前值小于自己这部分最大值,则意味着两件事。
// 第一件事便是,因为当前值移动,所以另一部分一定大于当前值。因为双指针逻辑是小的移动
// 第二件事便是,因为当前值小自己这部分最大值,所以在自己位置一定能装下 这部分最大值-自己值的水
// 这里这部分最大值<另一边显然,不然,另一边就会移动
// 所以秉承着这样的想法,整个双指针法就得到了证明,下面来实现代码逻辑
int l=0, r=height.length-1, lmax=0, rmax=0, res=0;
while(l<r){
if(height[l]<=height[r]){
if(height[l]>lmax){
lmax = height[l];
}
else{
res += lmax-height[l];
}
l++;
}
else{
if(height[r]>rmax){
rmax = height[r];
}
else{
res += rmax-height[r];
}
r--;
}
}
return res;
}
具体的なアイデアと理解の証拠はメモに書かれています。
次はスタックです。
public int trap(int[] height) {
//果然就很快
//下面尝试用栈的方式找
//栈的思路在于从左开始往右走
//如果栈空,入栈
//如果小于栈顶,入栈(其实就是小于前一个数)
//如果大于,则一直与栈里的元素比较,计算相互匹配上的area,直到当前柱子比栈顶柱子低(其实就是与左边柱子计算中间能放多少雨水)
int ans = 0, current = 0;
Deque<Integer> stack = new LinkedList<Integer>();
while (current < height.length) {
while (!stack.isEmpty() && height[current] > height[stack.peek()]) {
//这里只要栈不为空,cur是不变的,因为右边高的柱子要一直和左边低的每个柱子进行匹配
int top = stack.pop();
if (stack.isEmpty())
break;
int distance = current - stack.peek() - 1;
int bounded_height = Math.min(height[current], height[stack.peek()]) - height[top];
ans += distance * bounded_height;
}
stack.push(current++);
}
return ans;
}