最大矩形

一、前言

问题来源LeetCode 85,难度:困难

问题链接:https://leetcode-cn.com/problems/maximal-rectangle/

 

二、题目

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例:

输入:
[
  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]
]
输出: 6

 

三、思路

三种解法

3.1 方法一 暴力解法

一个坐标,我们约定在这个坐标右上方查找,找出包含这个坐标的最大矩形。所有坐标对于最大矩形即为所求。看下图分析,我们一坐标(1,1)为例:

 

1.第一列最大面积是3

 

2.包含第二列最大面积是4

 

3.包含第三列最大面积是3

坐标(1,1) 最大面积为4。找出所有坐标最大面积中最大值即为所求。

 

复杂度分析

  • 时间复杂度:O(n2m2)。
  • 空间复杂度:O(1) 。不需要额外的空间。

 

3.2 方法二:对方法一优化

方法一中求一个坐标最大面积,进行了横纵坐标遍历,在这个坐标上层也进行了一次横纵坐标遍历,我们可以在第一次横纵坐标遍历的时候,找出每一行到有几个连续的1,看图理解:

上图为坐标图,下图为横向连续为1的坐标即宽度。如果坐标点获得是从左往右,从下往上,则坐标点最大面积搜索需要与获得坐标点方向相反——向下搜索。以坐标(2,2)为例:

1.当前这一行最大面积为:2 = 2*1

2.包含下一行最大面积为:4 = 2*2

有方法一中两个方法搜索,变为一个方法搜索(向下)

 

复杂度分析

  • 时间复杂度:O(n2m)。
  • 空间复杂度:O(nm) 。需要数组记录点的宽度。

 

3.3 方法三:栈实现

方法二中,我们只计算以当前坐标为中心,向下搜索。我们继续观察一下这个图:

是不是可以找出这一列中包含的最大的面积,这样我们又可以省去一次遍历。参考《柱状图中最大的矩形多种解法》

《柱状图中最大的矩形多种解法》https://blog.csdn.net/nie2314550441/article/details/106305399

 

复杂度分析

  • 时间复杂度:O(nm)。
  • 空间复杂度:O(n) 。需要数组记录点的宽度。

 

四、编码实现

//==========================================================================
/**
* @file : MaximalRectangle.h
* @title: 最大矩形
* @purpose : 给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
*
* 示例:
* 输入:
* [
*   ["1","0","1","0","0"],
*   ["1","0","1","1","1"],
*   ["1","1","1","1","1"],
*   ["1","0","0","1","0"]
* ]
* 输出: 6
*
* 来源:力扣(LeetCode)
* 难度:困难
* 链接:https://leetcode-cn.com/problems/maximal-rectangle
*
*/
//==========================================================================
#pragma once
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;

#define NAMESPACE_MAXIMALRECTANGLE namespace NAME_MAXIMALRECTANGLE {
#define NAMESPACE_MAXIMALRECTANGLEEND }
NAMESPACE_MAXIMALRECTANGLE

// 方法一:动态规划
// 复杂度分析
// 时间复杂度:O(n2m2)。
// 空间复杂度:O(1) 。不需要额外的空间。
class Solution_1 
{
public:
    int maximalRectangle(vector<vector<char>>& matrix) 
    {
        // 参数判断 start
        if (matrix.empty())
        {
            return 0;
        }

        int h = matrix.size();
        int w = matrix[0].size();
        for (int i = 0; i < h; ++i)
        {
            if (w != matrix[i].size())
            {
                return 0;
            }
        }
        // 参数判断 end

        int maxRectangle = 0;
        for (int i = 0; i < h; ++i)
        {
            for (int j = 0; j < w; ++j)
            {
                if (matrix[i][j] == '1')
                {
                    int num = maximalRectangle(matrix, w, h, j, i);
                    maxRectangle = num > maxRectangle ? num : maxRectangle;
                }
            }
        }

        return maxRectangle;
    }

private:
    // 右上方向搜索
    int maximalRectangle(vector<vector<char>>& matrix, int w, int h, int wi, int hi)
    {
        if (wi < 0 || wi >= w || hi < 0 || hi > h)
        {
            return 0;
        }

        if (matrix[hi][wi] == 0)
        {
            return 0;
        }

        int maxRectangle = 1;
        int maxWide = 0;
        int tempw = w;
        for (int i = hi; i < h; ++i)
        {
            maxWide = 0;
            for (int j = wi; j < tempw; ++j)
            {
                if (matrix[i][j] != '1')
                {
                    tempw = j;
                    break;
                }
                ++maxWide;
            }

            if ((i - hi + 1) * maxWide > maxRectangle)
            {
                maxRectangle = (i - hi + 1) * maxWide;
            }

            if (maxWide == 0)
            {
                break;
            }
        }

        return maxRectangle;
    }
};

// 方法二:动态规划
// 复杂度分析
// 时间复杂度:O(n2m)。
// 空间复杂度:O(n*m) 。需要数组记录点的宽度。
class Solution_2
{
public:
    int maximalRectangle(vector<vector<char>>& matrix)
    {
        // 参数判断 start
        if (matrix.empty())
        {
            return 0;
        }

        int h = matrix.size();
        int w = matrix[0].size();
        for (int i = 0; i < h; ++i)
        {
            if (w != matrix[i].size())
            {
                return 0;
            }
        }
        // 参数判断 end

        vector<vector<int>> dp;
        dp.resize(w);
        for (int i = 0; i < w; ++i)
        {
            dp[i].resize(h);
        }
        vector<int> tp;
        tp.resize(h);
        int maxRectangle = 0;
        for (int i = 0; i < h; ++i)
        {
            for (int j = 0; j < w; ++j)
            {
                if (matrix[i][j] == '1')
                {
                    dp[j][i] = j == 0 ? 1 : dp[j-1][i] + 1;
                    int num = maximalRectangle(dp[j], i);
                    
                    maxRectangle = num > maxRectangle ? num : maxRectangle;
                }
                else
                {
                    dp[j][i] = 0;
                }
            }
        }

        return maxRectangle;
    }

private:
    // 左下方向搜索
    int maximalRectangle(vector<int> dp, int index)
    {
        if (index < 0 || index >= dp.size())
        {
            return 0;
        }

        if (dp[index] == 0)
        {
            return 0;
        }

        int maxRectangle = dp[index];
        int minWide = dp[index];
        --index;
        for (int i = 2; index >= 0; ++i, --index)
        {
            if (dp[index] < 1)
            {
                break;
            }
            else
            {
                minWide = min(minWide, dp[index]);
                maxRectangle = max(maxRectangle, minWide * i);
            }
        }

        return maxRectangle;
    }
};

// 方法三:栈
// 复杂度分析
// 时间复杂度:O(nm)。
// 空间复杂度:O(n) 。需要数组记录点的宽度。
class Solution_3
{
public:
    // 横向计数
    int maximalRectangle(vector<vector<char>>& matrix)
    {
        // 参数判断 start
        if (matrix.empty())
        {
            return 0;
        }

        int h = matrix.size();
        int w = matrix[0].size();
        for (int i = 0; i < h; ++i)
        {
            if (w != matrix[i].size())
            {
                return 0;
            }
        }
        // 参数判断 end

        vector<int> dp(h,0);
        int maxRectangle = 0;
        for (int j = 0; j < w; ++j)
        {
            for (int i = 0; i < h; ++i)
            {
                dp[i] = matrix[i][j] == '1' ? dp[i] + 1 : 0;
                maxRectangle = max(maxRectangle, maximalRectangle(dp));
            }
        }

        return maxRectangle;
    }

    // 纵向计算 和maximalRectangle方法类似
    int maximalRectangleEX(vector<vector<char>>& matrix)
    {
        if (matrix.empty())
        {
            return 0;
        }

        int h = matrix.size();
        int w = matrix[0].size();
        for (int i = 0; i < h; ++i)
        {
            if (w != matrix[i].size())
            {
                return 0;
            }
        }

        vector<int> dp(w, 0);
        int maxRectangle = 0;
        for (int i = 0; i < matrix.size(); ++i)
        {
            for (int j = 0; j < matrix[0].size(); ++j)
            {
                dp[j] = (matrix[i][j] == '1') ? dp[j] + 1 : 0;
            }
            maxRectangle = max(maxRectangle, maximalRectangle(dp));
        }

        return maxRectangle;
    }

private:
    int maximalRectangle(vector<int>& heights)
    {
        stack<int> st;
        heights.push_back(0);//结尾虚拟柱子高度0
        int size = heights.size();
        int res = 0;
        for (int i = 0; i < size; ++i)
        {
            while (!st.empty() && heights[st.top()] >= heights[i])
            {
                int val = st.top();
                st.pop();
                res = max(res, heights[val] * (st.empty() ? i : (i - st.top() - 1)));//宽度不包含当前元素
            }
            st.push(i);
        }
        return res;
    }
};



以下为测试代码//
// 测试 用例 START
void test(const char* testName, vector<vector<char>> & matrix, int expect)
{
    Solution_1 s1;
    Solution_2 s2;
    Solution_3 s3;


    int result1 = s1.maximalRectangle(matrix);
    int result2 = s2.maximalRectangle(matrix);
    int result3 = s3.maximalRectangle(matrix);

	if (result1 == expect && result2 == expect && result3 == expect)
	{
		cout << testName << ", solution passed." << endl;
	}
	else
	{
		cout << testName << ", solution failed. result1: " << result1 << ", result2: " << result2 << ", result3: " << result3 << endl;
	}
}

void Test1()
{
    vector<vector<char>> matrix{ {'1'} };
	int expect = 1;

	test("Test1()", matrix, expect);
}

void Test2()
{
    vector<vector<char>> matrix{ {'1','1','1','1','1','1'} };
	int expect = 6;

	test("Test2()", matrix, expect);
}

void Test3()
{
    vector<vector<char>> matrix{ {'1','0','1','1','1','0'} };
	int expect = 3;

	test("Test3()", matrix, expect);
}

void Test4()
{
    vector<vector<char>> matrix{ {'1','1','1','1','1','1'} };
	int expect = 6;

	test("Test4()", matrix, expect);
}

void Test5()
{
    vector<vector<char>> matrix{ {'1','0','1','1','1','0'} };
	int expect = 3;

	test("Test5()", matrix, expect);
}

void Test6()
{
    vector<vector<char>> matrix
    { 
        {'1','0','1','0','0'}, 
        {'1','0','1','1','1'},
        {'1','1','1','1','1'},
        {'1','0','0','1','0'},
    };
	int expect = 6;

	test("Test6()", matrix, expect);
}

void Test7()
{
    vector<vector<char>> matrix
    {
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},
        {'1','1','0','1','1'},
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},

    };
	int expect = 15;

	test("Test7()", matrix, expect);
}

void Test8()
{
    vector<vector<char>> matrix
    {
        {'1','0','0','1','1'},
        {'1','1','1','0','1'},
        {'0','1','1','1','1'},
        {'1','1','0','1','1'},
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},

    };
	int expect = 10;

	test("Test8()", matrix, expect);
}

void Test9()
{
    vector<vector<char>> matrix
    {
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},
        {'1','1','1','1','1'},

    };
	int expect = 30;

	test("Test9()", matrix, expect);
}

NAMESPACE_MAXIMALRECTANGLEEND
// 测试 用例 END
//


void MaximalRectangle_Test()
{
	NAME_MAXIMALRECTANGLE::Test1();
	NAME_MAXIMALRECTANGLE::Test2();
	NAME_MAXIMALRECTANGLE::Test3();
	NAME_MAXIMALRECTANGLE::Test4();
	NAME_MAXIMALRECTANGLE::Test5();
	NAME_MAXIMALRECTANGLE::Test6();
	NAME_MAXIMALRECTANGLE::Test7();
	NAME_MAXIMALRECTANGLE::Test8();
	NAME_MAXIMALRECTANGLE::Test9();
}

执行结果:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值