原題描述
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 height = [2,1,5,6,2,3],
return 10.
簡單的說,給出一個直方圖,求里面可以畫出的最大的矩形的面積
動態規划解題思路
1、求出左數組:左數組的定義為,里面存放,從當前數組下標對應位置盡可能的往左走(前面的元素一定要比自己高),最左邊所對應的元素。
2、求出右數組:定義和左數組相同,但是方向向右。
3、利用左右數組和每個位置的高度求出面積的最大值。
這樣講我自己都覺得抽象,還是由圖形來說明。
總結一下這個算法的思想:假設當前高度x直方向左走最多走到那里,向右最多走到那里,向左右走的時候都能走比它高的,因為和低的不能組成矩形。
然后就是當前高度下最長的矩形了,然后計算每個高度下最大的矩形就能得到最大值了。
其中利用動態規划思想的地方是在:向左走的時候,如果前一個元素比你高,那么你可以利用前一個元素已經求出的左數組中的值從這個值直接再向前找。
舉例來說:165432,現在3的左數組已經求出來的是【2】也就是下標為2的數字6,當要求2對應的左數組的值,因為3大於2,所以你可以直接利用3左數組中的值【2】因為大於3肯定大於2,所以可以瞬間往前走好幾步。
如果還是不理解,那就看看代碼吧。
動態規划解題代碼
/** *求直方圖最大面積-dp **/ #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; //數組元素個數 int n=6; //保存直方圖的高度 (前后用一個-1標記) int high[8]={-1,2,1,5,6,2,3,-1}; //定義左右數組 int leftArray[8]={-1,0,0,0,0,0,0,-1}; int rightArray[8]={-1,0,0,0,0,0,0,-1}; void getLeft() { for(int i=1;i<=n;i++) { int k = i; while(high[i] <= high[k-1]) k = leftArray[k-1]; leftArray[i] = k; } } void getRight() { for(int i=n;i>=1;i--) { int k = i; while(high[i] <= high[k+1]) k = rightArray[k+1]; rightArray[i] = k; } } int getMax() { int maxArea = 0; for(int i=1;i<=n;i++) { int temp=0; temp = (rightArray[i]-leftArray[i]+1) * high[i]; if(temp > maxArea) maxArea = temp; } return maxArea; } int main() { //求左數組 getLeft(); //求右數組 getRight(); //求最大值 cout<<getMax()<<endl; return 0; }
利用棧解題思路
下面是棧的解題思路。巧妙的利用棧里計算全部可能的面積,求最大值。
利用棧解題的代碼
/** *求直方圖最大面積-stack **/ #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> #include <stack> using namespace std; //數組元素個數 int n=6; //保存直方圖的高度 (前后用一個-1標記) int high[6]={2,1,5,6,2,3}; int main() { int i=0, maxArea=0,tempArea=0,popNum=0; stack<int> map; for(i=0; i<n; i++) { if(map.empty() || high[i] > high[map.top()]) map.push(i); else { popNum = map.top(); map.pop(); if(map.empty()) { tempArea = i * high[popNum]; } else { tempArea = (i-map.top()-1) * high[popNum]; } if(tempArea > maxArea) maxArea = tempArea; i--; } } while(!map.empty()) { popNum = map.top(); map.pop(); if(map.empty()) { tempArea = n * high[popNum]; } else { tempArea = (n-map.top()-1) * high[popNum]; } if(tempArea > maxArea) maxArea = tempArea; } cout<<"max area = "<<maxArea<<endl; return 0; }
總結和思考
個人認為其實dp的方式更容易讓人理解一些,但是從代碼量上面來說,確實用棧會快一些。
不夠只能說下一次解題如果沒有棧這個模版的話,比較容易寫出的還是dp的方式。
另外說一下,雖然dp的方式有for循環嵌套while循環,但是還是能保證時間復雜度還是O(N),和棧的方式是相同的。
只能說棧這個方法確實很巧妙,所以要記錄一下。








