【C#算法实现】求最大子矩阵的大小

前言

本文是【程序员代码面试指南(第二版)学习笔记】C#版算法实现系列之一,用C#实现了《程序员代码面试指南》(第二版)栈和队列中的求最大子矩阵的大小


一、题目要求

给定一个仅包含0和1的二维数组,要求找到最大的全部都是1的子矩阵,并返回该矩阵1的个数。

二、算法设计及代码实现

2.1 算法思想

概述:要就找到最大的子矩阵,最简单则是采取穷举法,但本文将采用遍历一次矩阵找出答案的方法进行解答。大体的过程是每一次遍历一行,遍历一行的时候记录这一行每一个元素所对应列的向上数的高度,也就是向上数有多少个连续的1。找到高度以后,就将该高度进行左右方向的延展,假如左右两边都比本身要高,那么就说明可以延展,则说明延展到了边界。这个时候找到的左右边界即宽,所需要的答案就是宽乘高。将每个元素都这样遍历计算以后找到最大的宽乘高就是答案。
记录高度是比较容易的,相对复杂的是找到左右边界。想要找到边界,其实就是要找到离自己最近的比自己低的高度,那么就可以采用单调栈进行计算。单调栈具体可以参考:最近的较小值序列(单调栈结构)
详细步骤
1、初始化记录高度的数组(仅需要n长度的数组即可,因为遍历每一行的时候可以重复利用)。
2、遍历第i行。初始化该行要使用的单调栈。
3、遍历第i行的第j个元素。
1)首先要记录高度,如果第j个元素为1,则将第j个高度+1;否则归零。
2)如果当前元素所对应的高度不高于左边的高度,说明上一个高度已经找到了右边界,之后要找到左边界。左边界为单调栈中栈顶序列,如果单调栈为空,左边界应该是-1。随后计算面积,面积应当为栈顶高度 * (j - 栈顶序列 - 1)。
4、当一行遍历结束以后,单调栈中可能还有残留的元素。遍历剩余的每一个元素,由于这些元素到最后也没有找到右边界,所以右边界就是行长度n。而左边界就是栈顶下面一个序列,所以面积应当为栈顶高度 * (n - left - 1)
5、找到最大的矩阵后返回结果。

计算面积示意图

2.2 代码实现

public static int MaxRectSize(int[,] matrix) {
    int m = matrix.GetLength(0);
    int n = matrix.GetLength(1);
    // 记录当前边为底向上的高度
    int[] height = new int[n];
    // 最大矩形面积
    int ans = 0;
    // 遍历每一行, 以当前行为底, 计算最大矩形面积
    for (int i = 0; i < m; i++) {
        // 单调栈, 用于判断每个位置的左右边界
        Stack<int> stack = [];
        // 遍历每一列, 计算当前列延展的最大矩形面积
        for (int j = 0; j < n; j++) {
            // 当前高度
            height[j] = matrix[i, j] == 0 ? 0 : height[j] + 1;
            // 找到右边界则出栈并计算面积
            while (stack.Count > 0 && height[stack.Peek()] >= height[j]) {
                int cur = stack.Pop();
                int left = stack.Count > 0 ? stack.Peek() : -1;
                ans = Math.Max(ans, height[cur] * (j - left - 1));
            }
            // 入栈
            stack.Push(j);
        }

        // 计算剩余栈中的面积
        while (stack.Count > 0) {
            int cur = stack.Pop();
            int left = stack.Count > 0 ? stack.Peek() : -1;
            ans = Math.Max(ans, height[cur] * (n - left - 1));
        }
    }
    return ans;
}
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值