LargestRectangleHistogram
- 問題描述:給定一個直方圖,假設每個bar的width爲1,高度不定。計算直方圖中所能構成最大矩陣的面積。比如下圖中的直方圖,所能構成的最大面積就是10.
-
- 分析:假設直方圖的左邊起點是L,右邊起點是R。則如果LR全用的話,則所構成的面積一定是(R−L)∗Hmin, Hmin表示是LR中最短的barmin的高度。而且只要我們使用了barmin,所構成的面積一定是小於等於(R−L)∗Hmin的。那如果我們不用呢?那我們就可以將所有的Bar劃分爲[L~barmin)和(barmin~R]。同樣我們就可以利用遞歸的思路來處理。
- 通過上面的分析,我們可以明顯的將其轉化成一個二分法的問題。
- Corn:輸入nums, L=0, R=N-1;
- 找到最短Bar的下標Index
- temp1 =(R−L+1)∗Hmin
- temp2 = Corn(nums, Index+1, R);
- temp3 = Corn(nums, L, Index-1);
- return min(temp1, temp2, temp3);
- 分析時間複雜度:因爲我們二分法裏面需要找L到R的最小值,所以我們的時間複雜度還是O(N^2).最壞情況下,如果nums是有序的(increase order),則我們不需要進行二分,通過O(N)的遍歷就可以:res=max(res,height[i]∗(R−i+1))。同樣decrease order也可以得到相同的結果。
- 代碼:
class Solution {
public:
int largestRectangleAreaBase(const vector<int >& heights, int l, int r){
if(l > r)
return 0;
int min_height = -1;
int min_height_index = l;
bool increase_sorted = true;
bool decrease_sorted = true;
for(int i=l;i<=r;i++){
if(min_height == -1 || min_height > heights[i]){
min_height = heights[i];
min_height_index = i;
}
if(i != r && heights[i] > heights[i + 1]){
increase_sorted = false;
}
if(i != r && heights[i] < heights[i + 1]){
decrease_sorted = false;
}
}
if(increase_sorted){
int res = 0;
for(int i=r;i>=l;i--){
res = max(res, heights[i] * (r-i+1));
}
return res;
}
if(decrease_sorted){
int res = 0;
for(int i=l;i<=r;i++){
res = max(res, heights[i] * (i-l+1));
}
return res;
}
int res = min_height * (r - l + 1);
res = max(res, max(
largestRectangleAreaBase(heights, l, min_height_index-1),
largestRectangleAreaBase(heights, min_height_index + 1, r)));
return res;
}
int largestRectangleArea(vector<int>& heights) {
int res = 0;
int l = 0;
int r = (int) heights.size()-1;
res = largestRectangleAreaBase(heights, l, r);
return res;
}
static void solution(){
vector<int> heights = {9, 8, 7, 6, 5, 4, 3, 2, 1};
Solution solution1;
cout<<solution1.largestRectangleArea(heights)<<endl;
}
};
MaximalRectangle
- 問題描述:輸入一個二維矩陣,裏面的元素都是0 或者 1。計算二維矩陣內全部由1構成的子矩陣的面積最大值。例如下圖所示,red box框中的就是最大的子矩陣。
- 解決思路:
- 將其可以轉化爲LargestRectangleHistogram的問題。
- 假設矩陣一共有M行,N列。
- 每一行,我們都有一個高度heights, 代表每一位從第1行到該行連續1的個數。通過這樣的轉化,我們每一行都可以看成一個histogram。
- 所以我們就是要計算每個histogram的最大矩陣。
- heights的值一開始初始化成0
- 如果matrix[i][j] == 1, heights[j] += 1
- 否則 heights[j] = 0
- 代碼:
int maximalRectangle(vector<vector<char>>& matrix) {
int m = (int) matrix.size();
if(m == 0)
return 0;
int n = (int) matrix[0].size();
vector<int > heights(n);
for(int i=0;i<n;i++)
heights[i] = 0;
int res = 0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
heights[j] = matrix[i][j] == '0' ? 0: heights[j] + 1;
}
res = max(res, largestRectangleArea(heights));
}
return res;
}
- 還有另一種思路就是動態規劃,DP
- 針對每個位置i, j 我們可以該位置的height,left, right值。
- height的定義和上面的方法一致。
- 因爲光知道height還是不行的,我們要計算width,而width就需要知道左右兩點的座標,這就是left 和 right的職責。
- 如果該點爲0,則left和right是什麼都無所謂,因爲我們的height是0. 但是因爲我們使用一維數組,所以left=0, right=N-1;
- 如果該點是1,假設其處在一個連續的1111序列中,座標的座標是L,右邊的左邊是R,則left = max(left, L). 這也就說明了爲什麼我們前面要將left置爲0,right置爲1。假設說上一行該列沒有1,則left就不受上一行的影響。如果上一行該列也有1,並且對應的left大於L的話,則我們應該取較大的那個,這裏相當於取交集,因爲該點的height也是大於1的。
- 可能有同學疑問,那如果一行很長的呢?其對應的area就是通過那些上下行都沒有1,計算得到的area。
- 代碼:
int maximalRectangleWithDP(vector<vector<char>>& matrix){
int m = (int) matrix.size();
if(m == 0)
return 0;
int n = (int) matrix[0].size();
int left[n];
int right[n];
int height[n];
memset(left, 0, sizeof(left));
for(int i=0;i<n;i++)
right[i] = n;
memset(height, 0, sizeof(height));
int res = 0;
for(int i=0;i<m;i++){
int cur_left = 0;
int cur_right = n-1;
for(int j=n-1;j>=0;j--){
if(matrix[i][j] == '1'){
right[j] = min(cur_right, right[j]);
}else{
right[j] = n-1;
cur_right = j-1;
}
}
for(int j=0;j<n;j++){
if(matrix[i][j] == '1'){
left[j] = max(left[j], cur_left);
height[j] = height[j] + 1;
}else{
left[j] = 0;
cur_left = j+1;
height[j] = 0;
}
res = max(res, (right[j] - left[j] + 1) * height[j]);
}
for(int j=0;j<n;j++){
cout<<left[j]<<",";
}
cout<<endl;
for(int j=0;j<n;j++){
cout<<right[j]<<",";
}
cout<<endl;
for(int j=0;j<n;j++){
cout<<height[j]<<",";
}
cout<<endl;
cout<<res<<endl<<endl;
cout<<endl;
}
return res;
}