方法一:暴力
遍歷所有柱子,對於一根柱子將它向左右擴展算出面積,若比最大面積大則更新最大面積。時間複雜度
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
if(len == 0)
return 0;
int ans = 0;
for(int i = 0;i < len;i++)
{
int now_num = heights[i];
int num = 0;
for(int j = 0;j < len;j++)
{
if(heights[j] >= now_num) //可以擴展
num += now_num;
else{ //無法擴展停下
ans = max(ans,num);
num = 0;
}
}
ans = max(ans,num);
}
return ans;
}
方法二:分治
上述方法複雜度爲,會超時。我們每次可以找到區間內的最小柱子,其所能勾勒出的面積爲柱子的高度乘以區間長度。每找到一個柱子可以將其左右劃分爲兩個子子問題,採用分治的思想解決問題。平均複雜度爲,但是當數組爲升序或者降序時複雜度還是爲。
題目所給函數的返回值爲int,但是heights[mid]*(r-l+1)
這個式子居然會爆int,需要將其改爲long long int,而且c++自帶的max函數的參數爲int類型,我們還需要自己寫一個比較。如果嫌上述操作麻煩我們可以用java代碼來實現。
c++版
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
if(len == 0)
return 0;
return Binary_search(0,len-1,heights);
}
int Binary_search(int l,int r,vector<int>& heights)
{
if(l > r)
return 0;
int mid = l;
for(int i = l;i <= r;i++)
{
if(heights[mid] > heights[i])
{
mid = i;
}
}
long long int a = heights[mid]*(r-l+1);
long long int b = max(Binary_search(mid+1,r,heights),Binary_search(l,mid-1,heights));
if(a < b)
{
long long int t = a;
a = b;
b = t;
}
return a;
}
java版
public int largestRectangleArea(int[] heights) {
int len = heights.length;
if(len == 0)
return 0;
return Binary_search(0,len-1,heights);
}
public int Binary_search(int l,int r,int[] heights)
{
if(l > r)
return 0;
int mid = l;
for(int i = l;i <= r;i++)
{
if(heights[mid] > heights[i])
{
mid = i;
}
}
return Math.max(heights[mid]*(r-l+1),Math.max(Binary_search(mid+1,r,heights),Binary_search(l,mid-1,heights)));
}
方法三:分治+線段樹
方法二最壞的時間複雜度爲,因爲分治最壞情況是,而且我們每次查詢一個區間的最小值的時間複雜度爲,當我們看到區間查詢就能想到線段樹了,我們通過線段樹來維護區間的最小值,就可以將查詢的複雜度降爲了,所以我們算法的最壞時間複雜度爲。
int tree[100000*3];
int cnt,min_num,indx;
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
if(len == 0)
return 0;
cnt = 0;
create_tree(0,len - 1,1,heights); //建樹
return Binary_search(0,len-1,heights);
}
int Binary_search(int l,int r,vector<int>& heights) //分治
{
if(l > r)
return 0;
min_num = heights[l];
indx = l;
find(l,r,0,heights.size()-1,1,heights);
int mid = indx; //由於indx爲全局變量,防止後面的操作改變其值,用mid接收它
long long int b = max(Binary_search(mid+1,r,heights),Binary_search(l,mid-1,heights));
long long int a = (long long int)heights[mid]*(r-l+1);
if(a < b)
{
long long int t = a;
a = b;
b = t;
}
return a;
}
void updata(int k,vector<int>& heights) //維護線段樹
{
//父節點的值爲左兒子和右兒子的最小值
if(heights[tree[k << 1]] < heights[tree[(k << 1) | 1]])
{
tree[k] = tree[k << 1];
}
else tree[k] = tree[(k << 1) | 1];
}
void create_tree(int l,int r,int k,vector<int>& heights) //建樹
{
if(r == l)
{
tree[k] = cnt++;
return;
}
int mid = (l + r) >> 1;
create_tree(l,mid,k << 1,heights);
create_tree(mid+1,r,(k << 1) | 1,heights);
updata(k,heights);
}
void find(int find_l,int find_r,int l,int r,int k,vector<int>& heights)//區間查詢
{
if(find_l <= l && r <= find_r)
{
if(min_num > heights[tree[k]])
{
min_num = heights[tree[k]];
indx = tree[k];
}
return;
}
int mid = (l + r) >> 1;
if(mid >= find_l) find(find_l,find_r,l,mid,k << 1,heights);
if(find_r >= mid+1) find(find_l,find_r,mid+1,r,(k << 1) | 1,heights);
}
方法四:單調棧
對於一個柱子我們要能確定它所能勾勒的最大面積的條件是:知道左邊和右邊比它小的柱子的位置。因爲只有碰到比它小的柱子時它才無法擴展,當它無法向左右擴展時說明此時是它所能勾勒的最大面積。方法一其實就是這個思想,但是它每次都要遍歷來尋找邊界位置,需要兩層循環,我們是否可以考慮將其邊界緩存下來,只用一次遍歷來完成操作。
我們維護一個單調棧,其單調性質爲從棧底到棧頂單調遞增(其實這樣我們就將左邊界緩存下來了)。
對於當前的一個柱子來說:
1.它比棧頂小則說明它是棧頂的右邊界,此時棧頂的最大面積已經確定可將其彈出,其最大面積的算法爲:。循環上述過程直到其滿足單調棧的性質將其入棧。
2.若它比棧頂大則將其入棧。
操作到最後,棧中還會剩餘一些柱子,這時所有柱子的右邊界都可以看成最大下標+1,當再剩一個柱子時其左邊界爲-1(表示沒有柱子可擴展)。
對於棧中的元素我們需要知道其高度和位置,我們只需要將其位置存在棧中,可通過數組索引來直到高度。
上述過程自己動手畫圖過一遍就可以明白。
由於每個柱子只入棧和出棧各一次,所以時間複雜度爲
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
if(len == 0)
return 0;
int ans = 0,indx;
stack<int>s;
s.push(-1);//添加哨兵
for(int i = 0;i < len;i++)
{
//找到右邊界
while(!s.empty() && s.top() >= 0 && heights[s.top()] > heights[i])
{
indx = s.top();
s.pop();
//將高度相同的柱子全部彈出
while(!s.empty() && s.top() >= 0 && heights[s.top()] == heights[indx])
{
indx = s.top();
s.pop();
}
if(!s.empty())
ans = max(ans,heights[indx]*(i - s.top() - 1));
}
s.push(i);
}
while(!s.empty())
{
indx = s.top();
s.pop();
if(!s.empty())
ans = max(ans,heights[indx]*(len - s.top() - 1));
}
return ans;
}