[程序员代码面试指南] 求最大子矩阵的大小

[程序员代码面试指南] 求最大子矩阵的大小

题目描述:

给定一个整型的矩阵,其中只有0和1两种值.

求其中 最大的全是1 的 矩形 区域的大小

如:
	1 1 1 0
	最大区域为 3
再如:
	1 0 1 1 
	1 1 1 1
	1 1 1 0
	最大矩阵区域为 6

分析:

  1. 将矩阵看做一行一行的.求每一行为底的,每个位置往上 1 的个数.用高度数组 height 表示.

    这里写图片描述

  2. 对于每一次切割获得的 height 数组,求其中每一个柱子,求能向左以及向右扩展到第一个比它小的地方.

使用栈来实现第二点:

从左到右遍历数组height,遍历到i元素:

  • i 位置的值大于栈顶位置所代表的值,直接将 i 入栈

    那么,这个栈保持的就是 递增 的.

  • 若当前元素 小于或者等于 当前元素 栈顶 位置所代表的值,

    那么就是要不断地出栈,直至栈顶元素所代表的值小于 i 表示的值.再把 i入栈.

    在弹出的过程中,

    假设当前弹出的栈顶位置为 j. 弹出之后,栈顶为 k

    1. 对于柱子 j 向右最远能扩到 i-1 .

      若所有元素入栈之后,那么栈不为空,则向右最远可以到 height.size ;

    2. 对于柱子 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;
}

体会:

  • 经常用来存储保证一个递增或者递减的性质,然后不满足性质,就通过出栈来更新要输出的信息.
  • 切割数组的方法,将二维矩阵看做一层层的.

参考文档

求最大子矩阵的大小+栈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值