3D接雨水——考察知识点

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.csdn.net/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;
    }


 

 

 

在Python中,“单调栈雨水”通常是一个经典的算法问题,源于一道面试题或编程比赛题目,它涉及到了数据结构中的栈操作和数组遍历。题目背景通常是这样的:给定一个高度不均匀的柱状图,每根柱子代表一个高度h[i]。下雨开始时,如果柱子顶部低于当前水位,那么雨会从顶部溢出,然后继续下落,直到水位再次高于柱子顶部。你的任务是在每次降雨之后,计算有多少雨水能够积累到每个柱子的底部。 这个问题的关键在于维护一个单调栈,当遇到比当前栈顶元素高的柱子时,就将这个高柱子压入栈中;当遇到比栈顶元素低的柱子时,则表示当前栈顶元素代表的是之前降雨中累积的高度,因此可以弹出栈顶元素并累计这部分雨水量。最后,剩余在栈中的柱子也都能积累雨水。 Python代码实现可能会这样: ```python def trap(height): if not height: return 0 stack = [] left, right = 0, len(height) - 1 water = 0 while left < right: # 当左指针处柱子高度小于等于栈顶柱子,把左指针处的雨水累积进结果 while stack and height[left] <= height[stack[-1]]: water += (height[stack.pop()] - height[left]) # 把左指针对应的柱子压入栈 stack.append(left) left += 1 # 当右指针处柱子高度大于等于栈顶柱子,把右指针处的雨水累积进结果 while stack and height[right] >= height[stack[-1]]: water += (height[right] - height[stack.pop()]) # 把右指针对应的柱子压入栈 stack.append(right) right -= 1 return water ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值