题目:最大正方形
Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area.
For example, given the following matrix:
1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0Return 4.
题意:
给定一个二维的由1和0字符填充的二进制矩阵,找到只包含1的最大的正方形,返回其面积。
思路一:
维护一个动态二维数组size,二维数组size的大小与给定的矩阵大小相同,初始化的时候将该矩阵初始化为0。二维数组的每一个数组值代表二维矩阵中直到下标位置时,最大正方形的边长是多少。首先利用二维矩阵将第一行与第一列对应的最大正方形边长存储在对应的二维数组中。之后从matrix[1][1]元素开始,只有当该元素左边元素,上边元素,左上面元素均为1字符时,最大正方形边长才能加1.即,在对应的size二维数组中,该元素的左边元素,上边元素,左上面元素三个值中最小的一个值才能决定当前元素所对应的值为多少。只有三个都为最大的,才能使最大正方形边长加1.
代码:C++版:20ms
class Solution { public: int maximalSquare(vector<vector<char>>& matrix) { int m = matrix.size(); if (!m) return 0; int n = matrix[0].size(); vector<vector<int>> size(m, vector<int>(n, 0));//存储每个元素处对应的最大正方形边长 int maxsize = 0; for (int j=0; j<n; j++) {//计算第一行元素对应的正方形边长 size[0][j] = matrix[0][j] - '0'; maxsize = max(maxsize, size[0][j]); } for (int i=1; i<m; i++) {//计算第一列元素对应的正方形边长 size[i][0] = matrix[i][0] - '0'; maxsize = max(maxsize, size[i][0]); } for (int i=1; i<m; i++) { for (int j=1; j<n; j++) { if (matrix[i][j] == '1') { //计算每个元素对应位置的最大正方形边长 size[i][j] = min(size[i-1][j-1], min(size[i-1][j], size[i][j-1])) + 1; maxsize = max(maxsize, size[i][j]); } } } return maxsize * maxsize; } };
思路二:
优化以上代码的空间复杂度。经上面分析可得,要更新size[i][j],只需要知道size[i][j - 1], size[i - 1][j - 1]和 size[i - 1][j]这三个位置的值即可,所以只需要两个m大小的整数数组即可。
代码:C++版:12ms
class Solution { public: int maximalSquare(vector<vector<char>>& matrix) { int m = matrix.size(); if (!m) return 0; int n = matrix[0].size(); vector<int> pre(m, 0); vector<int> cur(m, 0); int maxsize = 0; for (int i=0; i<m; i++) { //处理第一列 pre[i] = matrix[i][0] - '0'; maxsize = max(maxsize, pre[i]); } for (int j=1; j<n; j++) { cur[0] = matrix[0][j] - '0'; //处理后面列中的第一个元素 maxsize = max(maxsize, cur[0]); for (int i=1; i<m; i++) { if (matrix[i][j] == '1') { cur[i] = min(cur[i-1], min(pre[i-1], pre[i])) + 1; maxsize = max(maxsize, cur[i]); } } swap(pre, cur); fill(cur.begin(), cur.end(), 0); } return maxsize * maxsize; } };
思路三:
继续对思路二代码的空间复杂度进行优化,只需要维持一个m+1的数组即可,利用一个常数变量存储左上角的元素,利用存储的这些信息即可更新当前的数。
代码:C++版:12ms
class Solution { public: int maximalSquare(vector<vector<char>>& matrix) { if (matrix.empty()) return 0; int m = matrix.size(), n = matrix[0].size(); vector<int> dp(m+1, 0); int maxsize = 0, pre = 0; for (int j=0; j<n; j++) { for (int i=1; i<=m; i++) { int temp = dp[i]; if (matrix[i-1][j] == '1') { dp[i] = min(dp[i], min(dp[i-1], pre)) + 1; maxsize = max(maxsize, dp[i]); } else dp[i] = 0; pre = temp; } } return maxsize * maxsize; } };
思路四:
把数组中的每一个点都当成正方形的左顶点来向右下扫描来寻找最大正方形。扫描步骤为,确定了左顶点后,再往下扫描的时候,正方形的竖边长就确定了,只需要找到横边即可,这时使用直方图原理,使其累加值能反映出上面的值是否全为1字符。
代码:C++版:252ms
class Solution { public: int maximalSquare(vector<vector<char>>& matrix) { int res = 0; for (int i=0; i<matrix.size(); ++i) { vector<int> v(matrix[i].size(), 0); for (int j=i; j<matrix.size(); ++j) { //向下扫描 for (int k=0; k<matrix[j].size(); ++k) { //向右扫描 if (matrix[j][k] == '1') ++v[k]; } res = max(res, getSquareArea(v, j-i+1)); } } return res; } int getSquareArea(vector<int> &v, int k) { //计算每次正方形的面积 if (v.size() < k) return 0; int count = 0; for (int i=0; i<v.size(); ++i) { if (v[i] != k) count = 0; else ++count; if (count == k) return k*k; } return 0; } };