leetcode中的接雨水问题
从2D到3D,考察的知识点完全不同:2D中要求掌握双指针算法技巧,3D中要求掌握广度优先搜索算法+优先队列使用技巧
针对这个3D接雨水问题,罗列知识点
BFS:
广度优先搜索算法
breadth first search / broad first search
DFS:
深度优先搜索算法
deep first search
数据结构——图的遍历算法,根据访问节点的顺序,分为广度优先搜索BFS和深度优先搜索DFS。
- 什么是图?一种灵活的数据结构,一般作为一种模型用来定义对象之间的关系或联系。
- 图的表示:对象由顶点(V)表示,而对象之间的关系或者关联则通过图的边(E)来表示。 图可以分为有向图和无向图,一般用G=(V,E)来表示图。经常用邻接矩阵或者邻接表来描述一副图。
广度优先搜索BFS
// 通常用队列(先进先出,FIFO)实现
// 初始化队列Q
Q={起点s};
标记s为已访问;
while(Q非空){
取QQ队首元素u;
u出队;
if(u==目标状态){
...
}
所有与u相邻且未被访问的点进入队列;
标记u为已访问;
}
使用队列保存未被检测的结点。结点按照宽度有限的次序被访问和进出队列。
--类似于树的按层次遍历的过程
--广搜例子:你的眼镜掉在地上之后,你趴在地板上找。你总是先摸最接近你的地方,如果没有,再摸远一点的地方……
BFS算法用到了优先级队列,注意队列中元素的比较操作重载!
深度优先搜索DFS
思想:一直往深处走,直到找到接或者走不下去为止
DFS(dep,...) //dep代表目前DFS的深度
{
if(找到解||走不下去了)
{
...
return;
}
枚举下一种情况,DFS(dep+1,...)
}
使用栈保存未被检测的结点,结点按照深度优先的次序被访问并以此被压入栈中,并以相反的次序出栈进行新的检测。
--类似于树的先根遍历
--深搜例子:走迷宫,你没有办法用分身术来站在每个走过的位置。不撞南墙不回头。
DFS算法用到了递归和回溯。
实际案例:
BFS在三维接雨水问题中的应用。
问题来源与描述:https://leetcode-cn.com/problems/trapping-rain-water-ii/
辅助理解参考:https://blog.youkuaiyun.com/weixin_42054167/article/details/91989108
解法:
struct RainNode {
int i, j, h;
RainNode(int ii, int jj, int hh) :i(ii), j(jj), h(hh) {}
bool operator <(const RainNode& root) const {
return h>root.h;
}
};
int trapRainWater(vector<vector<int>>& heightMap) {
if (heightMap.empty()) return 0;
int m = heightMap.size(), n = heightMap[0].size();
int area = 0, h = 0; //h模拟水池外的海平面上升,m*n矩阵可以看做一个可以蓄水的水池
// 存放访问过的格子,且每次执行时删除高度最小的格子
// 存放的过程中,会将当前最小高度的几个格子放在前几个位置(后面的并不按顺序排放)
priority_queue<RainNode> q; //优先级队列,该队列中是将高度小的放在队首
vector<vector<bool>> visit(m, vector<bool>(n, false)); //m*n矩阵记录高度块是否被访问过
// 最外围的高度块是不可能蓄水的,放入优先级队列中,且记录为已被访问过
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (i == 0 || i == m - 1 || j == 0 || j == n - 1)
{
q.push(RainNode(i, j, heightMap[i][j]));
visit[i][j] = true;
}
}
}
// 一个点的x和y坐标的四邻域,按照上下左右的顺序排列
vector<int> x = { 0,0,-1,1 }, y = { -1,1,0,0 };
while (!q.empty())
{
auto f = q.top(); //取出队列q中最小的节点
// 当前格子的水平面高于海平面,海平面上升
// 否则,计算海水流进低于海平面的格子
if (h < f.h)
h++;
else
{
q.pop(); // 删除队列中最小的节点
// 当前格子的四个邻域的格子,当坐标不超出水池矩阵范围且没有被访问过时,
for (int k = 0; k < 4; k++)
{
int i = f.i + x[k], j = f.j + y[k];
if (i >= 0 && i < m && j >= 0 && j < n && visit[i][j] == false)
{
int hh = heightMap[i][j];
if (hh < h)
area += h - hh;
q.push(RainNode(i, j, hh));
visit[i][j] = true;
}
}
}
}
return area;
}