[leetcode] LargestRectangleHistogram MaximalRectangle

LargestRectangleHistogram

  • 問題描述:給定一個直方圖,假設每個bar的width爲1,高度不定。計算直方圖中所能構成最大矩陣的面積。比如下圖中的直方圖,所能構成的最大面積就是10.
    -在這裏插入圖片描述
  • 分析:假設直方圖的左邊起點是L,右邊起點是R。則如果LR全用的話,則所構成的面積一定是(RL)Hmin(R-L)*H_{min}, HminH_{min}表示是LR中最短的barminbar_{min}的高度。而且只要我們使用了barminbar_{min},所構成的面積一定是小於等於(RL)Hmin(R-L)*H_{min}的。那如果我們不用呢?那我們就可以將所有的Bar劃分爲[Lbarmin)[L~bar_{min})(barminR](bar_{min}~R]。同樣我們就可以利用遞歸的思路來處理。
  • 通過上面的分析,我們可以明顯的將其轉化成一個二分法的問題。
    • Corn:輸入nums, L=0, R=N-1;
    • 找到最短Bar的下標Index
    • temp1 =(RL+1)Hmin(R-L+1)*H_{min}
    • 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](Ri+1))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){
        // target: 計算一個矩陣中,全部由1組成的子矩陣的最大面積
        int m = (int) matrix.size();
        if(m == 0)
            return 0;
        int n = (int) matrix[0].size();
        int left[n];   // 針對每一行,如果該位置是1,則連續是1的,且位於最左邊的位置
        int right[n]; // 針對每一行,如果該位置是1,則連續是1的,且位於最右邊的位置
        int height[n]; // 針對每一行的,連續是1 的高度
        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;
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章