【算法】单调栈题单——矩阵系列⭐

news2025/1/14 20:27:36

文章目录

  • 题目列表
    • 84. 柱状图中最大的矩形(单调栈找左右两边第一个更低的位置)
    • 85. 最大矩形⭐⭐⭐⭐⭐
      • 解法1——使用柱状图的优化暴力方法
      • 解法2——单调栈 :归因到 84. 柱状图中最大的矩形 🐂
    • 1504. 统计全 1 子矩形⭐
  • 相关链接

题目列表

84. 柱状图中最大的矩形(单调栈找左右两边第一个更低的位置)

https://leetcode.cn/problems/largest-rectangle-in-histogram/description/

在这里插入图片描述

提示:
1 <= heights.length <=10^5
0 <= heights[i] <= 10^4

以 heights[i] 为高度的矩形,它的宽是* (r[i] - l[i] - 1);
其中 r[i] 和 l[i] 为左右两边第一个更低的位置。

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        // 记录左右第一个比它小的下标
        int[] l = new int[n], r = new int[n];
        Arrays.fill(l, -1);     // 左边第一个大于heights[i]
        Arrays.fill(r, n);      // 右边第一个小于等于heights[i]
        Deque<Integer> stk = new ArrayDeque<>();
        for (int i = 0; i < n; ++i) {
            while (!stk.isEmpty() && heights[i] <= heights[stk.peek()]) {
                r[stk.pop()] = i;
            }
            if (!stk.isEmpty()) l[i] = stk.peek();
            stk.push(i);
        }
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            int s = heights[i] * (r[i] - l[i] - 1);
            if (s > ans) ans = s;
        }
        return ans;
    }
}

从具体结果来看,r[i] 表示的是小于等于的而不是小于的,这看起来会出错但其实并不会。
原因在于,如果h[i]和h[j]是相同的高度,那么r[i]=j这是错误的,但是r[j]可以得出正确的答案。
具体看下面的例子:
image.png

85. 最大矩形⭐⭐⭐⭐⭐

https://leetcode.cn/problems/maximal-rectangle/description/

在这里插入图片描述

提示:
rows == matrix.length
cols == matrix[0].length
1 <= row, cols <= 200
matrix[i][j] 为 '0' 或 '1'

解法1——使用柱状图的优化暴力方法

https://leetcode.cn/problems/maximal-rectangle/solutions/535672/zui-da-ju-xing-by-leetcode-solution-bjlu/

在这里插入图片描述
在这里插入图片描述


总结一下思路:

  1. 预处理出 left[i][j],是每个点作为右下角时,它的左边包括多少连续的1。
  2. 枚举每个点作为右下角,计算其最大面积。随着高度的变大,宽度会随之减小。
class Solution {
    public int maximalRectangle(char[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        // 构造 left 数组
        int[][] left = new int[m][n];
        for (int i = 0; i < m; ++i) {
            if (matrix[i][0] == '1') left[i][0] = 1;
            for (int j = 1; j < n; ++j) {
                if (matrix[i][j] == '1') {
                    left[i][j] = left[i][j - 1] + 1;
                }
            }
        }

        int ans = 0;
        // 枚举每个点(i,j)作为右下角
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                int width = left[i][j], area = width * 1;
                // 尝试扩展高度
                for (int k = i - 1; k >= 0; --k) {
                    width = Math.min(width, left[k][j]);
                    area = Math.max(area, width * (i - k + 1));
                }
                ans = Math.max(ans, area);
            }
        }
        return ans;
    }
}

解法2——单调栈 :归因到 84. 柱状图中最大的矩形 🐂

解法1 需要枚举每个点,对于每个点又有 m 的复杂度,所以时间复杂度是 O ( m 2 ∗ n ) O(m^2*n) O(m2n)

可以优化成求解每一行 m,对于每一行,相当于 84. 柱状图中最大的矩形 的时间复杂度,总的复杂度是 O ( m ∗ n ) O(m*n) O(mn)

在这里插入图片描述

class Solution {
    public int maximalRectangle(char[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        int[] h = new int[n];
        int[] l = new int[n], r = new int[n];
        int ans = 0;
        for (int i = 0; i < m; ++i) {
            // 处理以第 i 行为底的矩形
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == '1') h[j]++;
                else h[j] = 0;
            }
            // 利用单调栈求解
            Deque<Integer> stk = new ArrayDeque<>();
            Arrays.fill(l, -1);
            Arrays.fill(r, n);
            for (int j = 0; j < n; ++j) {
                while (!stk.isEmpty() && h[j] < h[stk.peek()]) {
                    r[stk.pop()] = j;
                }
                if (!stk.isEmpty()) l[j] = stk.peek();
                stk.push(j);
            }
            int area = 0;
            for (int j = 0; j < n; ++j) {
                area = Math.max(area, h[j] * (r[j] - l[j] - 1));
            }
            if (area > ans) ans = area;
        }
        return ans;
    }
}

1504. 统计全 1 子矩形⭐

https://leetcode.cn/problems/count-submatrices-with-all-ones/description/

在这里插入图片描述

提示:
1 <= m, n <= 150
mat[i][j] 仅包含 0 或 1

解法1——枚举 O ( m 2 ∗ n ) O(m^2*n) O(m2n)

class Solution {
    public int numSubmat(int[][] mat) {
        int m = mat.length, n = mat[0].length, ans = 0;
        int[][] left = new int[m + 1][n + 1];
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (mat[i - 1][j - 1] == 1) {
                    left[i][j] = left[i][j - 1] + 1;
                }

                int w = left[i][j];
                for (int k = i; k >= 1 && w > 0; --k) {
                    w = Math.min(w, left[k][j]);
                    ans += w;	// 每个高度有w个宽度可选
                }
            }
        }
        return ans;
    }
}

解法2——单调栈优化⭐⭐⭐⭐⭐(比较难理解,细细品味)

配合官解食用,https://leetcode.cn/problems/count-submatrices-with-all-ones/solutions/336410/tong-ji-quan-1-zi-ju-xing-by-leetcode-solution/

在这里插入图片描述
在这里插入图片描述

为了复用上面的结果,需要用单调栈维护一个单调非递减的序列,只有这样才能满足:该层的 sum 是上层的 sum + 该层的 w
否则,应该去除上层的额外宽度 w,同时保留其去除额外宽度之后的高度 h,加入当前层,再送入栈中。

class Solution {
    public int numSubmat(int[][] mat) {
        int m = mat.length, n = mat[0].length, ans = 0;
        int[][] left = new int[m + 1][n + 1];
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (mat[i - 1][j - 1] == 1) {
                    left[i][j] = left[i][j - 1] + 1;
                }
            }
        }
        // 枚举每一列
        for (int j = 0; j < n; ++j) {
            Deque<int[]> stk = new ArrayDeque<>();  // 记录元素是[left[][], height]
            // 从上到下枚举每一行
            for (int i = 0, sum = 0; i < m; ++i) {
                int height = 1;                     // 初始高度为1
                // 维护宽度单调递增的单调队列
                while (!stk.isEmpty() && stk.peek()[0] > left[i + 1][j + 1]) {
                    // 去除无效的额外宽度
                    sum -= stk.peek()[1] * (stk.peek()[0] - left[i + 1][j + 1]);
                    height += stk.pop()[1];         // 继承移除的高度
                }
                sum += left[i + 1][j + 1];          // 矩形数量加上当前层的宽度
                ans += sum;
                stk.push(new int[]{left[i + 1][j + 1], height});
            }
        }
        return ans;
    }
}

相关链接

【算法】单调栈题单(矩阵系列、字典序最小、贡献法)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1281429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于媒体查询不能生效的原因

问题 今天写媒体查询&#xff0c;遇到了个问题&#xff0c;卡了很久&#xff0c;引入三个样式&#xff1a;mainPageCommon.css、mainPageBig.css、mainPageSmall.css。其中的两个样式可以生效&#xff0c;但是小尺寸的媒体查询不能生效&#xff0c;这里很奇怪&#xff01;&…

STM32F407-14.3.10-01PWM模式

PWM 模式 脉冲宽度调制模式可以生成一个信号&#xff0c;该信号频率由 TIMx_ARR⑩ 寄存器值决定&#xff0c;其占空比由 TIMx_CCRx⑤ 寄存器值决定。 通过向 TIMx_CCMRx 寄存器中的 OCxM⑰ 位写入 110 &#xff08;PWM 模式 1&#xff09;或 111 &#xff08;PWM 模式 2&#…

C++学习之路(十七)C++ 用Qt5实现一个工具箱(增加托盘图标并且增加显示和退出菜单)- 示例代码拆分讲解

上篇文章&#xff0c;我们用 Qt5 实现了在小工具箱中添加了《为屏幕颜色提取功能增加一个点击复制的功能》功能。今天我们增加一个比较正式点的功能&#xff0c;就是增加托盘图标并且增加显示和退出菜单&#xff08;越来越像回事了吧 &#x1f601; &#xff09;。下面我们就来…

ssm医院门诊互联电子病历管理信息系统源码和论文

摘 要 网络的广泛应用给生活带来了十分的便利。所以把医院门诊互联电子病历管理与现在网络相结合&#xff0c;利用java技术建设医院门诊互联电子病历管理信息系统&#xff0c;实现医院门诊互联电子病历的信息化。则对于进一步提高医院门诊互联电子病历管理发展&#xff0c;对…

【电机控制】PMSM无感foc控制(五)相电流检测及重构 — 单电阻采样

0. 前言 相电流采样再FOC控制中是一个关键的环节&#xff0c;鉴于成本和易用性&#xff0c;目前应用较多的相电流采样方式是分流电阻采样&#xff0c;包括单电阻、双电阻以及三电阻采样法。 本章节先讲解单电阻采样相电流的检测及重构技术&#xff0c;在下一章讲解双电阻和三电…

项目实战一-性能测试筑基

这里写目录标题 一、为什么程序会出现性能问题、性能问题是怎么出现的&#xff1f;二、功能测试和性能测试的区别是什么&#xff1f;三、核心性能指标1、用户角度核心a、响应时间&#xff1a;b、并发量 2、成本角度3、运维角度面试题、并发量和吞吐量得区别&#xff1f;a、吞吐…

Qt 如何操作SQLite3数据库?数据库创建和表格的增删改查?

# 前言 项目源码下载 https://gitcode.com/m0_45463480/QSQLite3/tree/main # 第一步 项目配置 平台:windows10 Qt版本:Qt 5.14.2 在.pro添加 QT += sql 需要的头文件 #include <QSqlDatabase>#include <QSqlError>#include <QSqlQuery>#include &…

【强化学习算法】Q-learning原理及实现

实现代码github仓库&#xff1a;RL-BaselineCode 代码库将持续更新&#xff0c;希望得到您的支持⭐&#xff0c;让我们一起进步&#xff01; 文章目录 1. 原理讲解1.1 Q值更新公式1.2 ε-greedy随机方法 2. 算法实现2.1 算法简要流程2.2 游戏场景2.3 算法实现 3. 参考文章 1. 原…

[架构之路-256]:目标系统 - 设计方法 - 软件工程 - 软件设计 - 架构设计 - 软件系统不同层次的复用与软件系统向越来越复杂的方向聚合

目录 前言&#xff1a; 一、CPU寄存器级的复用&#xff1a;CPU寄存器 二、指令级复用&#xff1a;二进制指令 三、过程级复用&#xff1a;汇编语言 四、函数级复用&#xff1a;C语言 五、对象级复用&#xff1a;C, Java, Python 六、组件级复用 七、服务级复用 八、微…

win10下使用内置Linux

Win10安装Ubuntu子系统 #推荐博客 https://www.cnblogs.com/xiaoliangge/p/9124089.html #推荐视频 https://www.bilibili.com/video/BV184411i7As?spm_id_from333.337.search-card.all.click #Ubuntu18.04安装教程 https://edu.csdn.net/skill/gml/gml-214229ddcc6a496ba175…

基于hadoop下的Kafka分布式安装

简介 Kafka是一种分布式流处理平台&#xff0c;它具有高吞吐量、可扩展性、可靠性、实时性和灵活性等优点。它能够支持每秒数百万条消息的传输&#xff0c;并且可以通过增加节点来增加吞吐量和存储容量。Kafka通过将数据复制到多个节点来实现数据冗余和高可用性&#xff0c;即使…

JAVA代码优化:Easy Excel(操作Excel文件的开源工具)

Easy Excel官网&#xff1a; EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel (alibaba.com) https://easyexcel.opensource.alibaba.com/ Easy Excel的特点和优势&#xff1a; 简单易用&#xff1a;Easy Excel提供了简洁的API&#xff0c;使用起来非常方便。开发…

xxl-job(分布式调度任务)

简介 针对分布式任务调度的需求&#xff0c;市场上出现了很多的产品&#xff1a; 1)TBSchedule&#xff1a;淘宝推出的一款非常优秀的高性能分布式调度框架&#xff0c;目前被应用于阿里&#xff0c;京东&#xff0c;支付宝&#xff0c;国美等很多互联网企业的流程调度系统中。…

Linux 基本语句_14_信号灯实验

原理&#xff1a; Send进程通过建立共享内存区域&#xff0c;并向其中写入数据&#xff0c;Recive通过与共享内存连接读取其中的数据。 但是如果进程进行读取操作的时候其他进程再次写入会产生数据丢失&#xff0c;产生竞态&#xff0c;为了确保在某段时间内只有一个操作&…

【Node.js】Node.js环境下载与安装教程(Windows系统)

前言 Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;可以让你使用JavaScript进行服务器端编程。本教程将向你展示如何在Windows系统上下载和安装Node.js环境。 下载 首先&#xff0c;你需要下载Node.js环境。 打开Node.js官方网站&#xff1a;https://no…

Leetcode2661. 找出叠涂元素

Every day a Leetcode 题目来源&#xff1a;2661. 找出叠涂元素 解法1&#xff1a;哈希 题目很绕&#xff0c;理解题意后就很简单。 由于矩阵 mat 中每一个元素都不同&#xff0c;并且都在数组 arr 中&#xff0c;所以首先我们用一个哈希表 hash 来存储 mat 中每一个元素的…

C语言中的动态内存管理

在C语言中&#xff0c;动态内存管理是通过一系列的标准库函数来实现的&#xff0c;这些函数包括malloc, free, calloc 和 realloc。它们允许程序在运行时动态地分配和释放内存&#xff0c;这是管理复杂数据结构&#xff08;如链表、树等&#xff09;时非常有用的功能。 为什么…

为何要3次握手?TCP协议的稳定性保障机制

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

MYSQL练题笔记-聚合函数-每月交易

一、题目相关内容 1&#xff09;相关的表和题目 2&#xff09;帮助理解题目的示例&#xff0c;提供返回结果的格式 二、初步的理解 是需要知道每个月和每个国家/地区的事务数及其总金额&#xff0c;每个月和每个国家/地区已批准的事务数及其总金额&#xff1b;以上的理解还是…

RPC和REST对比

RPC和REST对比 参考学习 RPC 和 REST 之间有什么区别&#xff1f; 当我们对比RPC和REST时&#xff0c;其实是在对比RPC风格的API和REST风格的API&#xff0c;后者通常成为RESTful API。 远程过程调用&#xff08;RPC&#xff09;和 REST 是 API 设计中的两种架构风格。API …