求解最大子矩阵的大小
【题目】
给定一个整型矩阵,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
例如:
1 1 1 0
其中,最大的矩形区域有3个1,所以返回3。再如:
1 0 1 1
1 1 1 1
1 1 1 0
其中,最大的矩形区域有6个1,所以返回6。
思路:
先计算以data[i]为底的矩阵大小,
例如:
1 0 1 1
1 1 1 1
1 1 1 0
以第一行为底,大小[1,0,1,1]
第二行为底,大小[2,1,2,2]
第三行为底,大小[3,2,3,0]
遇到0就置为0,故用 height[i] = data[i*cols+j]==0 ? 0 : height[i]+1;
如果遇到0,就置0,否则,高度+1,得到一个以data[i]为底的矩阵大小;
实际上可以看作一个直方图:例如:以第三行为底,可以得到
转化为在直方图的计算就是,以同一边作为底,当前最大能覆盖的矩阵面积:
注意,
每一个计算得到的直方图,都需要进行计算一下最大矩阵,例如用例4:
显然,第二三四五行中,得到的height矩阵,都是[0,0,0,0,0]
最大矩阵在第一行,
接下来计算最大矩阵:
例如 h e i g h t [ ] = { 1 , 4 , 3 , 2 , 4 , 5 } height[] = \{1,4,3,2,4,5\} height[]={
1,4,3,2,4,5}
定义栈stack,
- 当前栈为空,或者遇到一个比当前栈顶大的值,压栈(放入
坐标i
,而不是height[i]
)。这样一来,栈里的坐标标注的矩阵,就都是递增的矩阵了。
例如这里的1,4,就可以直接入栈了,此时栈中是他们所在的坐标: [ 0 , 1 ] [0,1] [0,1]
-
遇到一个和当前栈顶一样大,或者比当前栈顶小的值:
i f ( ! s t a c k . e m p t y ( ) & & h e i g h t [ i ] < = h e i g h t [ s t a c k . t o p ( ) ] ) if(!stack.empty()\ \ \&\& \ \ \ height[i]<=height[stack.top()]) if(!stack.empty() && height[i]<=height[stack.top()])
说明矩阵大小开始收缩了,
此时以当前遍历到的j = stack.top()
为中心,向左右进行扩展
此时遇到了3,j = stack.top()
,所以j=1
可以计算高度为4的矩阵的最大大小了,
这里,4向左,只能遍历到坐标为1的位置,也就是它自身
4向右,只能遍历到左边为1的位置,也就是它自身
所以4所在位置的最大矩阵大小只能是4;
此时,栈中,j被弹出,再压入当前i,修改后的坐标为: [ 0 , 2 ] [0,2] [0,2] -
可知若 h e i g h t [ i ] < h e i g h t [ s t a c k . t o p ( ) ] height[i]<height[stack.top()] height[i]<height[stack.top()],
j
向右,最多遍历到i-1
。因为如果可以遍历到i
,那就可以直接继续压栈了,由于height[i]<=height[stack.top()]
而且j = stack.top()
,所以最多能遍历到i-1
, -
如果 h e i g h t [ i ] = = h e i g h t [ s t a c k . t o p ( ) ] height[i]==height[stack.top()] height[i]==height[stack.top()] 那么其实
j
向右,是可以压栈到i
的,我们没有必要计算两次,因为i出栈的时候,也会计算一次向左的矩阵大小,显然这个矩阵是包含了j的。 -
j位置向左,最多可以扩展到新的栈顶k+1的位置,因为当前栈里面的矩阵高度一定是递增的,显然,
height[k]<height[j]
,所以此时可以得到,在j位置,最大矩阵面积是 ( i − k − 1 ) ∗ h e i g h t [ j ] (i-k-1)*height[j] (i−k−1)∗height[j] -
注意,只有在出栈时,才会计算最大矩形的面积。
index = 3 时,height[3] = 2,此时stack中有 [ 0 , 2 ] [0,2] [0,2],可知,以index=2的矩阵,向左有最大矩阵S = ((i-k-1)*height[j]) = 6
-
当i已经到了height矩阵的最后一个元素,可以令k=-1,i=height.length,继续计算出栈j的值,直到栈中为空为止。
c++
#include <iostream>
#include <stack>
using namespace std;
/**
** 获取以height为底的矩形的最大面积
**/
int GetMaxArea(int* height, int length) {
stack<int> s;
int j = 0, k = 0;
int maxArea = 0;
int i = 0;
while (i<length || !s.empty())
{
while (!s.empty() && height[i] <= height[s.top()])
{
j = s.top();
s.pop();
if (s.empty())
{
k = -1;
}
else {
k = s.top();
}
maxArea = max(maxArea, (i - k - 1) * height[j]);
}
if (i<length)
{
s.push(i);
i++;
}
}
return maxArea;
}
/**
* 获取最大子矩阵的面积大小
**/
int GetMaxSubMatrixArea(int* data,int rows,int cols) {
if (data==nullptr||rows<=0||cols<=0)
{
return 0;
}
int* height = new int[cols];
for (int i = 0; i < cols; i++)
{
height[i] = 0;
}
int max = 0,area = 0;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
height[j] = data[i * cols + j] == 0? 0 :height[j] + 1 ;
}
area = GetMaxArea(height, cols);
if (max<area)
{
max = area;
}
}
return max;
}
void GetMaxSubMatrixArea_Test() {
int data[] = {
1,0,1,1,
1,1,1,1,
1,1,1,0 };
// 6
cout << GetMaxSubMatrixArea(data, 3, 4) << endl;
int data2[] = {
1,0,1,1,
0,1,1,0,
1,1,1,1,
0,1,1,1,
1,1,1,1 };
// 9
cout << GetMaxSubMatrixArea(data2, 5, 4) << endl;
int data3[] = {
1,0,0,1,0,
0,1,1,0,1,
1,1,1,1,1,
1,1,1,1,0,
0,1,1,0,0 };
// 8
cout << GetMaxSubMatrixArea(data3, 5, 5) << endl;
int data4[] = {
0,1,1,1,1,
0,0,0,0,0,
0,0,0,0,0,
0,0,0,0,0,
0,0,0,0,0 };
// 4
cout << GetMaxSubMatrixArea(data4, 5, 5) << endl;
int data5[] = {
0,1,1,1,1,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0 };
// 5
cout << GetMaxSubMatrixArea(data5, 5, 5) << endl;
int* data6 = nullptr;
// 0 异常输入
cout << GetMaxSubMatrixArea(data6, 0, 0) << endl;
}
int main()
{
GetMaxSubMatrixArea_Test();
}