[程序员代码面试指南] 求最大子矩阵的大小
题目描述:
给定一个整型的矩阵,其中只有0和1两种值.
求其中 最大的全是1 的 矩形
区域的大小
如:
1 1 1 0
最大区域为 3
再如:
1 0 1 1
1 1 1 1
1 1 1 0
最大矩阵区域为 6
分析:
-
将矩阵看做一行一行的.求每一行为底的,每个位置往上
1
的个数.用高度数组height
表示. -
对于每一次切割获得的
height
数组,求其中每一个柱子,求能向左以及向右扩展到第一个比它小的地方.
使用栈来实现第二点:
从左到右遍历数组height,遍历到i元素:
-
若
i
位置的值大于栈顶位置所代表的值,直接将i
入栈那么,这个栈保持的就是
递增
的. -
若当前元素
小于或者等于
当前元素栈顶
位置所代表的值,那么就是要不断地出栈,直至栈顶元素所代表的值小于
i
表示的值.再把i
入栈.在弹出的过程中,
假设当前弹出的栈顶位置为
j
. 弹出之后,栈顶为k
-
对于柱子
j
向右最远能扩到i-1
.若所有元素入栈之后,那么栈不为空,则向右最远可以到
height.size
; -
对于柱子
j
,向左最远可以扩展到k+1
. (为什么是k+1
呢,因为k
位置肯定比j
位置的小,入栈时候的规则,保证了这个栈是递增的)若栈此时为空,那么它可以最远扩展到0.
-
实现:
int maxRecSize(vector<vector<int>> map) {
// 异常处理
if (map.size() == 0) {
return 0;
}
if (map[0].size() == 0) {
return 0;
}
int rows = map.size();
int cols = map[0].size();
vector<int> height(cols, 0);
int res = 0;
// 更新每一层为底的最大矩阵大小
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
// 更新height数组
height[j] = map[i][j] == 0 ? 0 : height[j] + 1;
}
res = max(res, maxRecFromBottom(height));
}
return res;
}
// 考察每一根柱子最大能扩散到哪里,找到柱子左边比它小的位置,以及找到右边比他小的位置.
int maxRecFromBottom(vector<int> height) {
int res = 0;
stack<int> m_stack; // 存放的是索引
for (int i = 0; i < height.size(); i++) {
while (!m_stack.empty() && height[i] <= height[m_stack.top]) {
int j = m_stack.top(); m_stack.pop();
int k = m_stack.empty() ? -1 : m_stack.top(); // 若为空,表示向左最远可以到0位置
int curArea = height[j] * ((i - 1) - (k + 1) + 1);
// 其是 height[j] * (i-k-1);
res = max(res, curArea);
}
m_stack.push(i);
}
while (!m_stack.empty()) { // 若栈不为空,表示向右最远可以到height.size()的位置
int j = m_stack.top(); m_stack.pop();
int k = m_stack.empty() ? -1 : m_stack.top();
int curArea = height[j] * ((height.size() - 1) - (k + 1) + 1);
// 其是 height[j] * (i-k-1);
res = max(res, curArea);
}
return res;
}
体会:
栈
经常用来存储保证一个递增或者递减的性质,然后不满足性质,就通过出栈来更新要输出的信息.- 切割数组的方法,将二维矩阵看做一层层的.