84原题:
Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3]
.
The largest rectangle is shown in the shaded area, which has area = 10
unit.
For example,
Given heights = [2,1,5,6,2,3]
,
return 10
.
解决思路:
刚开始打算从动态规划入手,试着写了一下递归方程,发现不合适。但是动态规划提供了一个思路,就是求以当前位置i为最矮柱形时的最大矩形面积,然后按i遍历求最大面积。
如何求以当前位置i为最矮柱形时的最大矩形面积,如果从最朴实的思路考虑,我们对于每个位置i,都分别向左向右遍历,找到对应矩形的左右边界,很容易求取矩形面积,这样的时间复杂度是O(n2)。
这个时间复杂度显然不符合我们的需要。可以考虑优化手段,在向左向右遍历的时候,对于相邻的i,显然有很多重复计算。我们可以设想,假如位置i的柱形,其左边界为Li,右边界为Ri。如果位于i+1的柱形比它更高,那么i+1的左边界是Li+1=i
设计方案:
设置栈s,遍历柱形,当遍历到的柱形高度大于栈顶高度时,入栈,否则,说明栈顶元素(curr)到达了其右边界(当前遍历到的位置i),弹出栈顶元素,计算其矩形面积,放在结果数组的对应位置。它的左边界等于出栈后栈顶元素pre的index(首先pre一定是矮于curr的,否则在遍历到curr时,由于curr矮于pre,pre会被弹出;其次pre至curr之间的元素一定是高于curr的,否则它们应该保留在栈中,pre不应该位于栈顶)。最终计算结果数组的最大值即可。时间复杂度O(n)。
代码:
#include
#include
#include
using namespace std;
int main(){
int hs[12] = {0,1,0,2,1,0,1,3,2,1,2,1};
vector heights;
for(int i = 0; i < 12; i++){
heights.push_back(hs[i]);
}
stack > stack;
vector results(6);
if(heights.size() == 0){
return 0;
} else {
stack.push(make_pair(0, heights[0]));
}
for(int i = 1; i < heights.size(); i++){
//遇到大的元素,弹出
while(stack.size() != 0 && stack.top().second > heights[i]){
//计算
pair curr = stack.top();
stack.pop();
int index = curr.first;
int height = curr.second;
int pre; //左边比curr小的元素坐标
if(stack.size() != 0){
pre = stack.top().first;
} else {
pre = -1;
}
int post = i;
results[index] = (post - pre - 1) * height;
}
stack.push(make_pair(i, heights[i]));
}
while(!stack.empty()){
pair curr = stack.top();
stack.pop();
int index = curr.first;
int height = curr.second;
int pre; //左边比curr小的元素坐标
if(stack.size() != 0){
pre = stack.top().first;
} else {
pre = -1;
}
int post = heights.size();
results[index] = (post - pre - 1) * height;
}
int max = 0;
for(int i = 0; i < results.size(); i++){
if(results[i] > max){
max = results[i];
}
}
cout << max << endl;
return max;
}
85原题:
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 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 0
Return 6.
解决思路:
这题如果做过84题就会有巧妙的思路,将N*M的矩阵压成N个大小为M的数组,将数组套到84题的函数中即可。
具体一点,对于第i行,将0-i行的元素“压扁”作为第i个数组,其第j个元素的值等于从i行向前数,连续的1的个数。如题例中,第三行对应的数组为[ 4, 0, 0, 3, 0],然后将数组丢给84行的函数,即求出以第i行为底的矩形的最大面积。最终对N行求最大即可。时间复杂度O(n2)。
代码:
#include
#include
#include
using namespace std;
int getLineResult(vector heights){
stack > stack;
vector results(heights.size());
if(heights.size() == 0){
return 0;
} else {
stack.push(make_pair(0, heights[0]));
}
for(int i = 1; i < heights.size(); i++){
//遇到大的元素,弹出
while(stack.size() != 0 && stack.top().second > heights[i]){
//计算
pair curr = stack.top();
stack.pop();
int index = curr.first;
int height = curr.second;
int pre; //左边比curr小的元素坐标
if(stack.size() != 0){
pre = stack.top().first;
} else {
pre = -1;
}
int post = i;
results[index] = (post - pre - 1) * height;
}
stack.push(make_pair(i, heights[i]));
}
while(!stack.empty()){
pair curr = stack.top();
stack.pop();
int index = curr.first;
int height = curr.second;
int pre; //左边比curr小的元素坐标
if(stack.size() != 0){
pre = stack.top().first;
} else {
pre = -1;
}
int post = heights.size();
results[index] = (post - pre - 1) * height;
}
int max = 0;
for(int i = 0; i < results.size(); i++){
if(results[i] > max) max = results[i];
}
return max;
}
int getMatrixResult(vector > heights){
vector results;
vector > new_heights;
int N = heights.size();
if(N == 0) return 0;
int M = heights[0].size();
if(M == 0) return 0;
for(int i = 0; i < N; i++){
vector a, b;
new_heights.push_back(b);
for(int j = 0; j < M; j++){
new_heights[i].push_back(0);
}
}
for(int i = 0; i < M; i++){
int tmp = 0;
for(int j = 0; j < N; j++){
if(heights[j][i] == '0'){
new_heights[j][i] = 0;
} else {
if(j == 0){
new_heights[j][i] = 1;
} else {
new_heights[j][i] += new_heights[j-1][i] + 1;
}
}
}
}
for(int i = 0; i < N; i++){
vector line = new_heights[i];
int r = getLineResult(line);
results.push_back(r);
}
int max = 0;
for(int i = 0; i < N; i++){
if(results[i] > max){
max = results[i];
}
}
return max;
}
int main(){
int a[4][5] = {{'1','0','1','0','0'},{'1','0','1','1','1'},{'1','1','1','1','1'},{'1','0','0','1','0'}};
vector > d;
for(int i = 0; i < 4; i++){
vector tmp;
for(int j = 0; j < 5; j++){
tmp.push_back(a[i][j]);
}
d.push_back(tmp);
}
cout << getMatrixResult(d) << endl;
}
后来看了大神们的源码,思想是类似的,数据结构更为简洁,可以看到分为height、left、right三个数组,表征的是,以当前的位置i,j所在的行作为矩形的底,以matrix[i][j]向上的连续的1的个数作为高的矩形,其高、左边界、右边界的值。(这里的左右边界与我们的解法中的左右边界分别大/小1,即表示的是“1”的取值范围)
行之间的数组存在一定的继承关系,height上的继承自不必说,左右边界具体表现在left[j] = max(left[j], currleft)与right[j] = min(right[j], currright)这两句。以左边界为例,当上一层为“0”时,left[j]继承来的值为0,currleft为从左边遍历来的左边界,max后取值正确;当上一层不为“0”时,对于对于上一层高为height[j]-1的左边界,如果在这一行相应的位置均为1的话,自然最好,left[j]保持不变;不行的话,取从左边取到的左边界currleft,left[j]=max(left[j], currleft)=currleft,是在将左边界尽量向右以满足矩形限制。
int maximalRectangle(vector>& matrix) {
if(!matrix.size() || !matrix[0].size()) return 0;
int m = matrix.size();
int n = matrix[0].size();
vector left(n, 0);
vector right(n, n);
vector height(n, 0);
int maxArea = 0;
for(int i = 0; i < m; i++)
{
int currleft = 0, currright = n;
for(int j = 0; j < n; j++)
{
if(matrix[i][j] == '1')
height[j]++;
else
height[j] = 0;
}
for(int j = 0; j < n; j++)
{
if(matrix[i][j] == '1')
left[j] = max(left[j], currleft);
else
{
left[j] = 0;
currleft = j + 1;
}
}
for(int j = n - 1; j >= 0; j--)
{
if(matrix[i][j] == '1')
right[j] = min(right[j], currright);
else
{
right[j] = n;
currright = j;
}
}
for(int j = 0; j < n; j++)
maxArea = max(maxArea, (right[j] - left[j]) * height[j]);
}
return maxArea;
}