算法設計與分析 第八週
最大矩形
1.題目描述
2.選題原因
本週學習了動態規劃的相關知識,對於動態規劃有了一定的瞭解,因此選擇了一道動態規劃的題目,加深自己對於動態規劃相關算法的理解。
3.題目分析及算法
3.1分析
這道題如果按照正常的思路走的話,無非就是從最邊角的點開始遍歷,每次遍歷的一個點,再以這個點開始向兩邊查找,直到遇到
0
,說明矩形終止,最後比較矩形,得到矩形的大小。
讓我們簡單的看一下這樣的思路下的算法:
3.2算法1.0
初始化最大矩陣:Max = 0;
遍歷矩陣中的所有點
對每一個點(a, b)
:
- 以此爲起點,開始向右,向下遍歷,遇到
0
則停下;記遍歷行爲x
,列爲y
。最後的矩形爲:(y - b) * (x - a)
。
下面我們來分析一下,很顯然,這種方法需要的時間複雜度非常大:對於每一個點,需要的複雜度爲從當前到最低端(最差情況),複雜度應該是O(n^3)
,顯然是我們不能夠接受的。
那麼我們有什麼方法可以改進呢?
3.2直方圖算法
考慮一個直方圖
如果我們有一個直方圖,以這個直方圖爲基礎,找直方圖的最大矩形是否好找呢?是非常好找的。我們只需要依次比較即可。那麼我們現在要做的就是如何將這個01矩陣轉化爲直方圖。
其實,畫直方圖也很好實現,考慮直方圖的特性:就是相鄰的1
豎着壘起來。對任意一個點來說,自己的值一定是自己頭頂的元素值+1
(自己是0
的時候除外)
如何實現呢?
從每一行開始計算,將以上的數據承載在本行,舉一個簡單的例子:
該元素爲3
,則證明頭上有2
個1
,如果元素爲1
,則證明頭頂是0
,如果該元素是0
呢,證明頭頂是0
,自己也是0
。
這樣,在每一行都找尋一遍最大元素,並不斷地更新最大值,即可得到。
那麼還有一個重要的問題,就是怎麼求一個直方圖中的最大值?一個比較普遍的解法就是剪枝法,就不贅述了,主要的思路就是每求一個點,都用這個點和之前的結果去比較,如果不會比之前更大,那麼也不用接着後面的計算,直接跳過即可。
我們的算法可以進一步改進:
3.3算法3.2
維護數組直方圖,初始化爲第一行的元素隊列;
以行爲單位開始遍歷:對於每一行:
- 遍歷每一個元素,若該元素爲
0
,則直方圖的該位置爲0
,否則爲直方圖該位置元素數值+1
- 用這一行裏最大的矩陣大小與維護值比較,取大的數。
3.4小例子
我們用一個簡單的小例子來解釋一下這個算法:
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
對於這樣的一個矩陣我們維護的直方圖應該是怎麼樣的呢?
第一行1, 0, 1, 0, 0
第二行2, 0, 2, 1, 1
第三行3, 1, 3, 2, 2
第四行4, 0, 0, 3, 0
4.關鍵代碼
4.1構建直方圖
int max = 0;
int row = matrix.size();
int col = matrix[0].size();
vector<int> height(col + 1, 0);
//從每一行開始遍歷
for (int i = 0; i < row; i++) {
stack<int> s; //維護當前最高的方塊的位置
for (int j = 0; j < col + 1; ++j) {
if (j < col) {
height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0;
}
4.2剪枝比較
//運用剪枝的方法,每得到一個點,就求到目前的最大值
//如果沒有最大的點大,那麼可以減去
while (!s.empty() && height[s.top()] >= height[j]) {
int cur = s.top();
s.pop();
//比較到當前點和維護的最大解
int temp_max = height[cur] * (s.empty() ? j : (j - s.top() - 1));
max = max > temp_max? max :temp_max;
}
s.push(j);
5.結果
6.源代碼
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
//去除空矩陣
if (matrix.size() == 0 || matrix[0].size() == 0) {
return 0;
}
int max = 0;
int row = matrix.size();
int col = matrix[0].size();
vector<int> height(col + 1, 0);
//從每一行開始遍歷
for (int i = 0; i < row; i++) {
stack<int> s; //維護當前最高的方塊的位置
for (int j = 0; j < col + 1; ++j) {
if (j < col) {
height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0;
}
//運用剪枝的方法,每得到一個點,就求到目前的最大值
//如果沒有最大的點大,那麼可以減去
while (!s.empty() && height[s.top()] >= height[j]) {
int cur = s.top();
s.pop();
//比較到當前點和維護的最大解
int temp_max = height[cur] * (s.empty() ? j : (j - s.top() - 1));
max = max > temp_max? max :temp_max;
}
s.push(j);
}
}
return max;
}
};