一维水坑面积
是的,如上,我是一个懒人
输入是一个数组,数组中的元素代表每一个位置的房子的高度,输出积水量。
举例输入
5 7 6 10 7 5 4 5 7
输出
7
思路
遍历数组的每一个元素,找到当前元素左边的最高值,右边的最高值,则改元素能形成的积水量=min(左边最高值,右边最高值)-当前元素值(当然前面的最小值要小于当前元素值)。
但这样直接的方式时间复杂度就是
O
(
n
2
)
O(n^2)
O(n2),考虑先遍历一次,将每个元素右边的最大值存储起来,然后再从左往右遍历一次,在这次遍历中,对于每一个元素先找到其左边的最大值(将当前值和左边最大值比较),然后计算当前位置的积水量,这样时间复杂度就是
O
(
n
)
O(n)
O(n),空间复杂度也是
O
(
n
2
)
O(n^2)
O(n2),因为用了一个数组来存每个元素右边的最大值。
class Solution {
public:
int trap(vector<int>& height) {
int water=0;
if(height.size()<3) return water;
int LL=0,RL=0;
vector<int> right(height.size());
for(int i=height.size()-1;i>=0;i--){
RL=max(height[i],RL);
right[i]=RL;
}
for(int i=0;i<height.size();i++){
LL=max(height[i],LL);
water+=min(LL,right[i])>height[i]?min(LL,right[i])-height[i]:0;
}
return water;
}
};
二维水坑面积问题(leetcode 407)
问题:给定一个m*n的矩阵,表示每一个块的高度,根据这个矩阵,计算能积水多少。
example:
Given the following 3x6 height map:
[
[1,4,3,1,3,2],
[3,2,1,3,2,4],
[2,3,3,2,3,1]
]
Return 4.

上图取自leetcode,是给定的矩阵的示意图。
上图同取自leetcode,蓝色部分表示能积攒的水量。
分析:
一维的情况是,当前块儿的高度低于两侧的高度,就能积水,同理,二维的情况时,当前块儿的高度低于它四周的高度就能积水。So,采取BFS遍历当前块儿周围的块儿,判断周围的高度是否比当前的高,高的话,周围的块儿的最高值和当前块儿的差值就是能存储的水量。当然,边界上的肯定不能积水,可以先处理边界的块儿,遍历边界的块儿挨着的中间的块儿。参考了一篇博客的内容,它讲述的十分详细且有图片示意。维护一个变量mx来表示当前的海平面高度,从1开始。每次从高度最小的块儿开始遍历,所以可以使用优先级队列来存储每个块儿,这样每次能返回高度最矮的块儿。一层高度的块儿遍历完之后,mx值加1,遍历第二层的块儿,以此类推,直到遍历完所有的块儿。同时,为了实现这个操作,需要维护一个矩阵,表示每个块是否被访问。
综上
- 定义一个mx变量,存储当前海平面高度;
- 需要定义一个优先级队列,里面存储每个块儿的高度和它的索引,为实现BFS则让每一次返回的都是高度最小的块儿,同时每遍历完一个块儿,就把它周围的块儿push进去;
- 定一个与输入矩阵一样大小的矩阵visited,记录每个块儿是否被访问过;
- 先将边界点放进优先级队列中,且都标记为已访问过;
- 遍历队列,每次获取高度最小的那个;
- 按照BFS的顺序遍历周围的块儿,更新mx,更新访问状态,将该块儿加入que;
- 更新res(若mx比当前块儿高,则res加上两者的差值)。
class Solution {
public:
int trapRainWater(vector<vector<int>>& heightMap) {
int res=0;
if(heightMap.empty()) return res;
int rows=heightMap.size(),cols=heightMap[0].size();
int mx=INT_MIN;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > que;
vector<vector<bool> > visited(rows,vector<bool>(cols,false));//记录每个块儿是否访问过
vector<vector<int> > direction={{1,0},{-1,0},{0,1},{0,-1}};//用来控制方向
//先将边界的块儿的高度放进优先级队列中
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
if(i==0 || i==rows-1 || j==0 ||j==cols-1){
que.push(make_pair(heightMap[i][j],i*cols+j));
visited[i][j]=true;
}
}
}
//遍历que
while(!que.empty()){
auto cur=que.top();//获取最矮的
que.pop();
int height=cur.first;
int x_row=cur.second/cols;//解压对应的行列索引
int y_col=cur.second%cols;
mx=max(mx,height);
for(int i=0;i<direction.size();i++){//按BFS的顺序
int x=x_row+direction[i][0];int y=y_col+direction[i][1];
if(x<0 || x>=rows ||y<0 || y>=cols ||visited[x][y]==true) continue;
visited[x][y]=true;//更新访问记录
res+=mx>heightMap[x][y]?mx-heightMap[x][y]:0;//更新res
que.push(make_pair(heightMap[x][y],x*cols+y));//更新que
}
}
return res;
}
};
总结
- 上面的代码参考上面的博客,que中存储的值是块儿的高度和对应的索引,用了一个十分巧妙的办法,将二维矩阵的索引进行了压缩。索引表示为
cols*i+j
,这样在后面遍历每个元素的时候,解压表示为x_row=cur.second/cols; y_col=cur.second%cols;
- 优先级队列的使用
#include <queue>
priority_queue<int> q; //从大到小出
priority_queue<int,vector<int>,less<int> > q; //同上
priority_queue<int,vector<int>,greater<int> > q; //从小到大出,P.S 最后那个> 要空一格,不然就成右移操作了
// greater也可以自定义,就像比较大小的函数,定义一个cmp,并传入
priority_queue<int,vector<int>,cmp> q;
//还可以定义结构体
//比如定义二叉树常用的一个node
priority_queue<node> q;
//basic op
q.empty();
q.size();
q.top();
q.pop();
q.push();
- pair的用法
//pair用于表示一种关联关系,key-value
//define
pair<string,int> p;
p.first='hello';
p.second=2;
pair<string,int> q1('world',3);
make_pair<string,int>('test',4);