leetcode 407.接雨水II(超级简单易懂)

这篇具有很好参考价值的文章主要介绍了leetcode 407.接雨水II(超级简单易懂)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

大家好,这里是张哈希刷题频道。今天给大家带来一道有意思的题目,"接雨水II"。

看到好多小伙伴,遇到了面试官想 `kill -9` 结束面试,抛出了这个3D接雨水核弹(前些年抛红黑树线段树之类的),接不下来基本上就宣布下线,此时一般可以直接带上简历走人了 ... (开个玩笑,可以继续厚脸皮硬刚,坚持住我们能赢)

leetcode 407.接雨水II(超级简单易懂),leetcode,算法,职场和发展leetcode 407.接雨水II(超级简单易懂),leetcode,算法,职场和发展

这个题目听起来很恐怖,其实是个老派难题。我们从头到尾把这个题目的解决过程讲解一遍。

题目分析

leetcode 407.接雨水II(超级简单易懂),leetcode,算法,职场和发展

首先来看题目,题目大意是给你一个二维的height map,让你求这个地形能够接的雨水总量。height map里面的每个格子的高度是不同的,雨水能存在低洼处但不能流到更高的地方,最后你要算出地形中雨水的总量。

这么一个题目最直观的解法就是边遍历边计算了,对于每个位置尝试遍历它的上下左右,找到它能够承载雨水的最大高度。然后四个方向找最小值,这个值就是该位置可以存放的雨水量。

上面的思路很简单,但是实现起来就比较麻烦了:你得维护一个可以随时找到四个方向最大高度的数据结构,对于每个位置都要计算四个方向的最大值。暴力做法,需要三层循环,所以时间复杂度 O(m * n * min(m, n))。

不过细想下来,这道题也不是非得上下左右计算不可,我们可以想个更巧妙的做法。

现在说一个时间复杂度比较高级的解法。

正餐

这个解法的思路也很易懂:从外围(上下左右四个边界)开始扫描,用一个小根堆维护当前 height map 剩余的低洼区域。

首先把四周的格子都加入这个小根堆, 然后从小根堆中取出高度最低的格子(并每次都维护一个max用于判断当前已经从最小堆中弹出的最高柱子,方便我们用 “高 - 低” 得出低洼的单位值), 看看它能不能接雨水。遍历它上下左右的格子, 观察能否蓄水。

leetcode 407.接雨水II(超级简单易懂),leetcode,算法,职场和发展

即:max = 1; max-heightMap[row][col] < 0; 

       Math.max(0, max-heightMap[row][col]) = 0,边界的低洼不接雨水

leetcode 407.接雨水II(超级简单易懂),leetcode,算法,职场和发展

如果能蓄水,就计算出它能蓄多少水,把蓄水量加到结果上(如果外围柱子标记为max后发现比四周的要低,则无法蓄水)。无论能不能蓄水,都把周围不在小根堆中的低洼格子加入小根堆,这样才不会漏掉中心区域的低洼区域。以此类推,最终小根堆中就只有最中心的高地,全部低洼区域都被遍历并计算过了。

不看也不会做,还得上代码:

   /**
     * 小根堆
     * 时间:O( mn * log(mn) )
     * 空间:O( m*n)
     * @param heightMap
     * @return
     */
    public int trapRainWater(int[][] heightMap) {
        int m = heightMap.length, n = heightMap[0].length, max = Integer.MIN_VALUE, ans = 0; // max:全局max
        PriorityQueue<Node> minHeap = new PriorityQueue<>(((o1, o2) -> o1.height - o2.height));
        boolean[][] visited = new boolean[m][n];
        // 1)将四周一圈加入小根堆:
        for (int col = 0; col < n; col++) {
            addToHeap(heightMap, minHeap, visited, 0, col, max);
            addToHeap(heightMap, minHeap, visited, m - 1, col, max);
        }
        for (int row = 0; row < m; row++) {
            addToHeap(heightMap, minHeap, visited, row, 0, max);
            addToHeap(heightMap, minHeap, visited, row, n - 1, max);
        }

        // 2)从小根堆弹出节点,弹出时更新全局max,再将其上下左右分别拉入小根堆,入堆时结算其位置的水量:
        while (!minHeap.isEmpty()) {
            Node node = minHeap.poll();
            max = Math.max(max, node.height); // 更新全局max
            ans += addToHeap(heightMap, minHeap, visited, node.row - 1, node.col, max);
            ans += addToHeap(heightMap, minHeap, visited, node.row + 1, node.col, max);
            ans += addToHeap(heightMap, minHeap, visited, node.row, node.col - 1, max);
            ans += addToHeap(heightMap, minHeap, visited, node.row, node.col + 1, max);
        }
        return ans;
    }

    // heightMap[row][col]入堆,并返回当前[row][col]位置的接水量
    private int addToHeap(int[][] heightMap, PriorityQueue<Node> minHeap, boolean[][] visited,
                          int row, int col, int max) {
        if (row >= 0 && row < heightMap.length && col >= 0 && col < heightMap[0].length && !visited[row][col]) {
            minHeap.add(new Node(heightMap[row][col], row, col));
            visited[row][col] = true;
            return Math.max(0, max - heightMap[row][col]);
        }
        return 0;
    }

    private static class Node {
        int height;
        int row, col;

        public Node(int height, int row, int col) {
            this.height = height;
            this.row = row;
            this.col = col;
        }
    }
  1. 首先,我们创建一个小根堆,用于存储每个单元格的高度和位置。同时,我们还需要一个布尔数组 visited 来记录哪些单元格已经被访问过。
  2. 然后,我们将地图四周的所有单元格加入到小根堆中,并标记为已访问。
  3. 接下来,我们开始主要的循环。在每次循环中,我们从小根堆中取出一个节点(即一个单元格),并更新全局最大高度 max。然后,我们将该节点的上、下、左、右四个邻居单元格加入到小根堆中,并计算每个邻居单元格的接水量。这个接水量等于全局最大高度 max 减去邻居单元格的高度,如果结果为负,则接水量为0。
  4. 最后,当小根堆为空时,我们就计算出了所有单元格的接水量,将它们相加,就得到了最终的答案。

核心代码35行,可以说很妙了,掐表20分钟基本不可能bugfree,背诵吧少年~

这里主要代码量就集中在 addToHeap 这个函数了,需要控制边界,并且做接雨水量的计算。接雨水量的计算就是 max(0, 局部最高高度 - 当前格子高度),这是因为如果局部最高高度比当前格子高度还要低,那就意味着当前低洼区已经遍历完了,不可能存水。

以上就是这道题目的的两个解法思路,前一种是用了左右两个辅助数组的暴力做法,后一种是基于小根堆的解法。

小根堆解法,外加入堆和弹出堆的操作,理论时间复杂度是 O(mn * log(mn))。文章来源地址https://www.toymoban.com/news/detail-832592.html

到了这里,关于leetcode 407.接雨水II(超级简单易懂)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LeetCode150道面试经典题-- 存在重复元素 II(简单)

    给你一个整数数组  nums 和一个整数  k ,判断数组中是否存在两个 不同的索引   i  和   j ,满足 nums[i] == nums[j] 且 abs(i - j) = k 。如果存在,返回 true ;否则,返回 false 。 示例 1:   输入:nums = [1,2,3,1], k = 3 输出:true 示例 2:   输入:nums = [1,0,1,1], k = 1 输出:true  示例

    2024年02月12日
    浏览(36)
  • day59 | 503.下一个更大元素II、42. 接雨水

    目录: 503.下一个更大元素II https://leetcode.cn/problems/next-greater-element-ii/ 给定一个循环数组  nums  (  nums[nums.length - 1]  的下一个元素是  nums[0]  ),返回  nums  中每个元素的  下一个更大元素  。 数字  x  的  下一个更大的元素  是按数组遍历顺序,这个数字之后的第一个

    2024年02月16日
    浏览(20)
  • 【LeetCode 算法】Linked List Cycle II 环形链表 II

    给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从

    2024年02月14日
    浏览(32)
  • Sutherland–Hodgman 算法介绍(简单易懂)

    目录 一、算法介绍 二、算法描述 三、计算细节补充 四、算法总结   我们使用 Sutherland–Hodgman算法 来裁剪多边形的边,一般是给你一个多边形顶点序列( P1,P2,P3,P4,… Pn) 让你裁剪,最终裁剪掉 裁剪多边形 的 外部 部分( 下图黑框就是裁剪多边形 )。 像这样: 裁剪多边形示意

    2024年01月16日
    浏览(46)
  • 单调栈part2 | ● 503.下一个更大元素II ● 42. 接雨水

    本篇我侧重与说一说,如何处理循环数组。 相信不少同学看到这道题,就想那我直接把两个数组拼接在一起,然后使用单调栈求下一个最大值不就行了! 确实可以! 将两个nums数组拼接在一起,使用单调栈计算出每一个元素的下一个最大值,最后再把结果集即result数组resiz

    2024年02月13日
    浏览(67)
  • RSA 加密解密算法实现(简单,易懂)!!!

    目录 一、什么是RSA算法 1.对称加密 2.非对称加密 3.非对称加密的应用 二、RSA算法的基础操作步骤 1.生成公钥和私钥 2.用公钥加密信息  3.用私钥解密信息 三、AC代码 六、RSA算法的测试  七、共勉     在计算机中常用的加密算法分为两类: 对称加密算法和非对称加密算法。

    2024年01月20日
    浏览(57)
  • 算法leetcode|90. 子集 II(rust重拳出击)

    给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。 1 = nums.length = 10 -10 = nums[i] = 10 面对这道算法题目,二当家的再次陷入了沉思。 穷举数组的所有子集,每个

    2024年02月04日
    浏览(31)
  • 算法leetcode|59. 螺旋矩阵 II(rust重拳出击)

    给你一个正整数 n ,生成一个包含 1 到 n 2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 1 = n = 20 面对这道算法题目,二当家的再次陷入了沉思。 可以每次循环移动一步,判断移到边界就变换方向,巧用数组可以减少逻辑判断的复杂性。 也可以每次循环

    2024年02月11日
    浏览(28)
  • 力扣(LeetCode)算法_C++——存在重复元素 II

    存在重复元素 II 给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) = k 。如果存在,返回 true ;否则,返回 false 。 示例 1: 输入:nums = [1,2,3,1], k = 3 输出:true 示例 2: 输入:nums = [1,0,1,1], k = 1 输出:true 示例

    2024年02月09日
    浏览(29)
  • 算法leetcode|63. 不同路径 II(rust重拳出击)

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? 网格中的障碍

    2024年02月16日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包