接雨水

本文详细介绍了接雨水问题的四种解决方案,包括暴力法、双指针法、动态规划和单调栈法,并对每种解法的核心思路、代码实现及复杂度分析进行了详细讲解。适合想要提升算法能力的程序员学习。
摘要由CSDN通过智能技术生成

接雨水

接雨水这道题目是非常经典的面试题,师兄字节面试的时候似乎就碰到了,所以把它抽出来研究了一下,主要是学习参考Leetcode上的各种评论和题解,总结了四种方法,其中三种是按列计算,还有一种是按区域行计算。最后一种方法虽然复杂度上没有双指针法简单,但是如果给我们的数组是流式输入,无法从后往前进行遍历的话,就只有最后一种方法有用了。

42 接雨水

题目描述

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

示例1:

在这里插入图片描述

输入: height = [ 0 , 1 , 0 , 2 , 1 , 0 , 1 , 3 , 2 , 1 , 2 , 1 ] [0,1,0,2,1,0,1,3,2,1,2,1] [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:

输入: height = [ 4 , 2 , 0 , 3 , 2 , 5 ] [4,2,0,3,2,5] [4,2,0,3,2,5]

输出: 9

提示:

  • n = = h e i g h t . l e n g t h n == height.length n==height.length

  • 0 < = n < = 3 × 1 0 4 0 <= n <= 3 \times 10^4 0<=n<=3×104

  • 0 < = h e i g h t [ i ] < = 1 0 5 0 <= height[i] <= 10^5 0<=height[i]<=105

解法一:暴力法

核心思路

仔细思考每一个单位的雨水高度的特点,可以发现对于每个单位而言,雨水的高度就是该单位左右最高高度中的较小值和自己高度的差(注意要从自己本身找起)。所以用暴力法,找到每一个单位左右最高高度,然后用两者中较小的高度减去自己的高度,然后将所有高度的水的高度相加,就得到了能接的雨水的总量。

关键就在于发现单位处的雨水高度就是左右最高处较小值和自己高度的差。

代码
class Solution{
   
public:
    int trap(vector<int> &height){
   
		int ans = 0;
        int n = height.size();
        for(int i=1;i<n-1;i++){
   
            int left_high=0 , right_high=0;
            for(int j=i;j>=0;j--){
   
                left_high = max(left_high,height[j]);
            }
            for(int j=i;j<n;j++){
   
                right_high = max(right_high,height[j]);
            }
            ans += min(left_high,right_high)-height[i]
        }
    	return ans;
	}  
};
复杂度分析
  • 时间复杂度: O ( n 2 ) O(n^2)
### 雨水问题的算法解决方案 #### 1. 暴力解法 暴力解法通过双重循环来遍历每一个位置并寻找左侧和右侧的最大高度,从而计算该位置可以存储的水量。这种方法的时间复杂度较高,为 O(n²),适用于理解问题的本质。 ```cpp int trap(vector<int>& height) { int n = height.size(), ans = 0; for (int i = 0; i < n; ++i) { int max_left = 0, max_right = 0; for (int j = i; j >= 0; --j) { //Search the left part for max bar size max_left = max(max_left, height[j]); } for (int j = i; j < n; ++j) { //Search the right part for max bar size max_right = max(max_right, height[j]); } ans += min(max_left, max_right) - height[i]; } return ans; } ``` 此方法虽然直观易懂,但在处理大规模数据集时效率低下[^2]。 #### 2. 动态编程(预处理) 为了提高效率,可以通过预先计算每个位置左右两侧的最大高度数组,在一次扫描中完成整个过程。这样做的时间复杂度降低至O(n),空间复杂度同样为O(n)。 ```cpp vector<int> l_max(height.size()), r_max(height.size()); for(int i=1;i<height.size();++i){ l_max[i]=max(l_max[i-1],height[i-1]); } for(int i=(int)height.size()-2;i>=0;--i){ r_max[i]=max(r_max[i+1],height[i+1]); } // Calculate water trapped at each index using precomputed arrays. ``` 这种方法不仅提高了速度还保持了一定程度上的可读性和简洁性[^3]。 #### 3. 双指针技术 采用两个指针分别指向数组两端向中心移动的方式解决问题。每次比较左端与右端较小者作为当前边界,并更新相应方向上的最大值以及累积水量直到相遇为止。这种方式能够在线性时间内解决问题而不需要额外的空间开销。 ```cpp int left = 0, right = heights.length - 1; int maxLeft = 0, maxRight = 0; while (left < right) { if (heights[left] <= heights[right]) { if (heights[left] >= maxLeft) { maxLeft = heights[left]; } else { result += maxLeft - heights[left]; } left++; } else { if (heights[right] >= maxRight) { maxRight = heights[right]; } else { result += maxRight - heights[right]; } right--; } } return result; ``` 这种策略有效地减少了内存占用的同时也保证了良好的执行性能[^5]。 #### 4. 单调栈结构 利用单调递减栈保存可能出现积水区域的位置索引。每当遇到高于栈顶元素的新柱子时,则意味着形成了封闭区间,此时可以根据宽度差乘以高差得到这部分面积累加到总水量上。最后返回累计的结果即为所求答案。 ```cpp stack<int> st; st.push(0); ans = 0; for(i = 1 ; i<n ; i++){ while(!st.empty() && a[st.top()]<=a[i]){ h=a[st.top()]; st.pop(); if(st.empty()) break; distance=i-st.top()-1; minHeight=min(a[st.top()],a[i])-h; totalWater+=distance*minHeight; } st.push(i); } ``` 这种方法巧妙地运用了栈的数据特性实现了高效求解[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值