leetcode link: 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

1 个人思路

个人思路(快慢指针):

  • 1 slow指针用于寻找非0低的柱子,fast寻找slow右侧第一个更大柱子。
  • 2 slow和fast中间的面积-slow和fast中间的柱子面积即为雨水面积。
  • 3 进行从左到右,和从右到左两次计算。

 

class Solution {
public:
    int find_next(vector& height, int slow){
        for(int i=slow+1; i<height.size(); i++){ if (height[i] >= height[slow]) return i;
        }
        return -1;
    }

    int go(vector &height){
        int total = 0;
        int fast;
        for (int slow = 0; slow < height.size() - 1; slow++) {
            if (height[slow]) {
                fast = find_next(height, slow);
                cout << "slow is " << slow << " fast is " << fast << endl;
                if (fast != -1) {
                    // 加上中间全部的面积
                    total += (fast - slow - 1) * min(height[slow], height[fast]);
                    // 减去中间突出块的面积
                    for (int j = slow + 1; j < fast; j++) {
                        total -= height[j];
                    }
                    cout << "total is " << total << endl;
                    slow = fast - 1;
                }
            }
        }
        return total;
    }

    int trap(vector &height) {
	int front_total = 0, back_total =0, total = 0;
    front_total = go(height);
    reverse(height.begin(), height.end());
	back_total = go(height);
    total = max(front_total, back_total);
	
	return total;
}
};
官方题解:暴力解法、双指针优化、单调栈。

2 暴力解法

暴力解法步骤:
  • 为1~(size-1)的元素寻找其左右两边最大的元素,然后计算面积
  • 寻找i左边最大的元素
  • 寻找i右边最大的元素
  • 计算面积
int trap(vector &height) {
	// 1 暴力解法,为1~(size-1)的元素寻找其左右两边最大的元素,然后计算面积
    int total = 0;
    for(int i=1; i left) left = height[j];
        }
        // 2 寻找i右边最大的元素
        int right = height[i];
        for(int j=i+1; j right) right = height[j];
        }
        // 3 计算面积
        int h = min(left, right) - height[i];
        if (h > 0) total += h;
    }
    return total;

3 双指针优化

双指针本质上和暴力解法差不多,但是既然暴力解法每次都需要计算i左边的最大值元素和i右边的最大值元素,那为何不使用数组存起来。

  • 1 从1到i计算i左边最大值
  • 2 从size()-1到i计算右边最大值
  • 3 列上计算面积

Notes:

  • 1 left和right数组初始化
  • 2 left和right数组的遍历顺序
  • 3 列上计算雨水面积
int trap(vector &height) {
        int total = 0;
        // 1 计算i左边最大值和右边最大值数组
        vector left = height;
        vector right = height;
        for(int i=1; i<height.size()-1; i++){ left[i] = max(left[i], left[i-1]); } for(int i=height.size()-2; i>=1; i--) {
            right[i] = max(right[i], right[i+1]);
        }
        // 2 计算面积
        for(int j=1; j<height.size()-1; j++){ int h = min(left[j], right[j]) - height[j]; if (h > 0) total += h;
        }
        return total;
    }

4 单调栈

该题需要寻找元素i的左边最大值和右边最大值,自然想到可以使用单调栈。 假设使用单调栈寻找右边更大元素,则单调栈内元素天然有序,从栈底到栈顶的顺序为越来越小,则我们只要找到右边大于栈顶的元素(模式为大小大),即可计算雨水面积。 Notes:
  • 1 需要注意当输入元素与栈顶元素相等时,先弹栈,再进栈

class Solution {
public:
    int trap(vector &height) {
        int total = 0;
        stack st;
        st.push(0);
        for(int i=1; i<height.size(); i++){
            if(height[i] == height[st.top()]){
                st.pop();
                st.push(i);
            }
            else if (height[i] < height[st.top()]){ st.push(i); }else{ while(!st.empty() && height[i] > height[st.top()]){
                    int middle = st.top(); st.pop();
                    if (!st.empty()){
                        int h = min(height[st.top()], height[i]) - height[middle];
                        int w = i - st.top() - 1;
                        int sum = h * w;
                        if (sum > 0) total += sum;
                    }
                }
                st.push(i);
            }
        }
        return total;
    }
};